<?php

Phar::mapPhar('opentok.phar');

require_once('phar://'.__FILE__.DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'autoload.php');

__HALT_COMPILER(); ?>
- ,                 vendor/autoload.php   UY   z      %   vendor/composer/autoload_classmap.php[ UY[ %      '   vendor/composer/autoload_namespaces.phpS  UYS  l      !   vendor/composer/autoload_psr4.php  UY  JJ      !   vendor/composer/autoload_real.php  UY  vC      #   vendor/composer/autoload_static.php UY ߏ-         vendor/composer/ClassLoader.php4  UY4  Q      !   vendor/composer/include_paths.php   UY   z      W   vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/ExceptionInterface.php  UY  .ö      ]   vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/InvalidArgumentException.php  UY  h7I      ]   vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/UnexpectedValueException.php
  UY
  "       G   vendor/doctrine/instantiator/src/Doctrine/Instantiator/Instantiator.php   UY   &      P   vendor/doctrine/instantiator/src/Doctrine/Instantiator/InstantiatorInterface.php~  UY~  :      4   vendor/firebase/php-jwt/src/BeforeValidException.phpa   UYa   (S6      0   vendor/firebase/php-jwt/src/ExpiredException.php]   UY]   z      #   vendor/firebase/php-jwt/src/JWT.php8  UY8        9   vendor/firebase/php-jwt/src/SignatureInvalidException.phpf   UYf   Y      "   vendor/guzzle/guzzle/phar-stub.phpQ  UYQ  ^ib7      5   vendor/guzzle/guzzle/phing/tasks/ComposerLintTask.php`  UY`  Waζ      >   vendor/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php.  UY.  
o̶      7   vendor/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php*  UY*  B
      @   vendor/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php  UY  ?      /   vendor/guzzle/guzzle/src/Guzzle/Batch/Batch.php  UY  s      6   vendor/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php  UY  ;Ͷ      =   vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.phpt  UYt  zHF      >   vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php  UY  ֶ      >   vendor/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php  UY  2      ?   vendor/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php  UY  ~      8   vendor/guzzle/guzzle/src/Guzzle/Batch/BatchInterface.phpM  UYM  ƿs|      >   vendor/guzzle/guzzle/src/Guzzle/Batch/BatchRequestTransfer.php  UY  q;      :   vendor/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.phpk  UYk  dj      @   vendor/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php  UY  #T      J   vendor/guzzle/guzzle/src/Guzzle/Batch/Exception/BatchTransferException.php
  UY
  &      A   vendor/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php  UY  E$      7   vendor/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php  UY  ۆP      6   vendor/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php  UY  `B2      8   vendor/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php  UY  j      >   vendor/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.phpG  UYG  !      =   vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php  UY  :ö      ?   vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php  UY        =   vendor/guzzle/guzzle/src/Guzzle/Cache/ClosureCacheAdapter.php^  UY^  ζ      >   vendor/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.phpe  UYe  S޶      :   vendor/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php  UY        9   vendor/guzzle/guzzle/src/Guzzle/Cache/Zf1CacheAdapter.php  UY  Z㍼      9   vendor/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php  UY  I̶      @   vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php  UY  %;      5   vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php-  UY-  E      0   vendor/guzzle/guzzle/src/Guzzle/Common/Event.php(  UY(  k۶      K   vendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php   UY   qS      H   vendor/guzzle/guzzle/src/Guzzle/Common/Exception/ExceptionCollection.php  UY  A˲      D   vendor/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.phpd   UYd   ض      M   vendor/guzzle/guzzle/src/Guzzle/Common/Exception/InvalidArgumentException.php   UY   VǶ      E   vendor/guzzle/guzzle/src/Guzzle/Common/Exception/RuntimeException.phpz   UYz   h^w      M   vendor/guzzle/guzzle/src/Guzzle/Common/Exception/UnexpectedValueException.php   UY   e      >   vendor/guzzle/guzzle/src/Guzzle/Common/FromConfigInterface.php  UY  1r      A   vendor/guzzle/guzzle/src/Guzzle/Common/HasDispatcherInterface.phpN  UYN  3       ;   vendor/guzzle/guzzle/src/Guzzle/Common/ToArrayInterface.php   UY   N      2   vendor/guzzle/guzzle/src/Guzzle/Common/Version.php  UY        D   vendor/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php  UY  a      :   vendor/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.phpz  UYz  b      /   vendor/guzzle/guzzle/src/Guzzle/Http/Client.phpA  UYA  F      8   vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php&  UY&  Pg5      8   vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlHandle.phps=  UYs=  X/޶      7   vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php8  UY8  {@      @   vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php=  UY=  њO      <   vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiProxy.php  UY  D      9   vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php  UY  \      =   vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php]  UY]  T      3   vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php  UY  á      <   vendor/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.phps	  UYs	        G   vendor/guzzle/guzzle/src/Guzzle/Http/Exception/BadResponseException.php4  UY4  =      O   vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php   UY   5"ڶ      P   vendor/guzzle/guzzle/src/Guzzle/Http/Exception/CouldNotRewindStreamException.php   UY   n      @   vendor/guzzle/guzzle/src/Guzzle/Http/Exception/CurlException.phpC  UYC  &Y      @   vendor/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php   UY         I   vendor/guzzle/guzzle/src/Guzzle/Http/Exception/MultiTransferException.php  UY  [ڒ      C   vendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php  UY  o~      O   vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php   UY   Wp      L   vendor/guzzle/guzzle/src/Guzzle/Http/Exception/TooManyRedirectsException.phpi   UYi   Ǹ      =   vendor/guzzle/guzzle/src/Guzzle/Http/IoEmittingEntityBody.php  UY  c"      @   vendor/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php#  UY#  C}#      G   vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php  UY  'DU      P   vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php  UY        D   vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php
  UY
  O      H   vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php   UY   hɶ      E   vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php  UY  ┶      N   vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php  UY        G   vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderInterface.phpP  UYP  (      <   vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/Link.php  UY        7   vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php  UY  S      A   vendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php	  UY	  ۓ`      9   vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFile.php  UY  M.>      B   vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php  UY  Ӑ¶      8   vendor/guzzle/guzzle/src/Guzzle/Http/Message/Request.phpK  UYK  1W߶      ?   vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php2  UY2  h      H   vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.phpx  UYx  &۶      A   vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestInterface.php7!  UY7!  Օ      9   vendor/guzzle/guzzle/src/Guzzle/Http/Message/Response.phpf  UYf  礶      2   vendor/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php  UY        H   vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php  UY  ^<Ͷ      L   vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php<  UY<  9|\      F   vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.phpt  UYt  $      Q   vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php  UY  )      4   vendor/guzzle/guzzle/src/Guzzle/Http/QueryString.php*"  UY*"  ö      <   vendor/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php#  UY#  `.      7   vendor/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php{'  UY{'  s;      9   vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem UY c      5   vendor/guzzle/guzzle/src/Guzzle/Http/StaticClient.php'  UY'  Z!      ,   vendor/guzzle/guzzle/src/Guzzle/Http/Url.php"9  UY"9  +a_      8   vendor/guzzle/guzzle/src/Guzzle/Inflection/Inflector.phpS  UYS  B#c      A   vendor/guzzle/guzzle/src/Guzzle/Inflection/InflectorInterface.php>  UY>  ָk      A   vendor/guzzle/guzzle/src/Guzzle/Inflection/MemoizingInflector.php  UY  l_t      C   vendor/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php  UY  \:      ;   vendor/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php  UY        <   vendor/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php  UY  GZ      ;   vendor/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php  UY  ڬ      8   vendor/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.phpT  UYT  R`1q      @   vendor/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.phpb  UYb  ]      :   vendor/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php   UY   p-      7   vendor/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.phpg  UYg  x c      9   vendor/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.phpG  UYG  #p      ;   vendor/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php  UY  \:      8   vendor/guzzle/guzzle/src/Guzzle/Log/MessageFormatter.php  UY  B      9   vendor/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php  UY  +$Y      5   vendor/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php^  UY^        5   vendor/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php  UY  Yڶ      5   vendor/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php  UY  >'      >   vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.phpz  UYz        G   vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php  UY  =      H   vendor/guzzle/guzzle/src/Guzzle/Parser/Message/AbstractMessageParser.php  UY  J2      @   vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php+  UY+  d      I   vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php  UY  (5;      H   vendor/guzzle/guzzle/src/Guzzle/Parser/Message/PeclHttpMessageParser.php  UY        9   vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php  UY        F   vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php  UY  QѶ      B   vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplate.php   UY   Y      K   vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php  UY  [      8   vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParser.php  UY  loV2      A   vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php  UY  "      <   vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php  UY  `      J   vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.phpI  UYI  Z4      S   vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php  UY  |      @   vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php	  UY	  (*      @   vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php=  UY=  Zm      K   vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php  UY  X|      J   vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CallbackBackoffStrategy.php  UY  >      J   vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php  UY  
jv      F   vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php  UY  %+      M   vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php  UY  mhS      F   vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/HttpBackoffStrategy.php  UY  #Ǧ      H   vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php  UY  T#      N   vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php  UY   sW      K   vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php  UY  AQp      J   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php   UY   k˶      <   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CachePlugin.php:3  UY:3  P      F   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php  UY  ]9      I   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CallbackCanCacheStrategy.php  UY  ,\p      J   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php  UY  [      H   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheKeyProvider.php  UY  V      D   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php   UY   ڶ      H   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php  UY        D   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php  UY  ܶ      A   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php  UY        F   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/RevalidationInterface.phpN  UYN  D      A   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/SkipRevalidation.php  UY  Nt      8   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php21  UY21        J   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php@  UY@  6.~      N   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.phpj  UYj  4`۶      I   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/FileCookieJar.php  UY  t2L      >   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php  UY  L"]      R   vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php   UY   !m|      B   vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.phpl  UYl  ˞      X   vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.phpV  UYV  |F      L   vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponsePlugin.php  UY  d'      Y   vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php   UY   ҙ      @   vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.phpQ  UYQ  DS+      8   vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php  UY  A"ڶ      F   vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php  UY  'f      A   vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.phpu  UYu  Nm      :   vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.phpN  UYN  p      <   vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php((  UY((  =,      @   vendor/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.phpq  UYq  7      B   vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php  UY        K   vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php  UY  S)      H   vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderLoader.phpD  UYD  _      ?   vendor/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php  UY        2   vendor/guzzle/guzzle/src/Guzzle/Service/Client.php%  UY%  
W      ;   vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php
  UY
        C   vendor/guzzle/guzzle/src/Guzzle/Service/Command/AbstractCommand.php40  UY40  Qb      B   vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php  UY  .      D   vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php  UY  %      L   vendor/guzzle/guzzle/src/Guzzle/Service/Command/CreateResponseClassEvent.php<  UY<   8+      L   vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php  UY  9[      I   vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php  UY  .'|      H   vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php`  UY`  d      L   vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php  UY  j1̶      P   vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.phpE  UYE  =      L   vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php  UY  ϠYc      F   vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/MapFactory.php  UY  	      U   vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.phpm  UYm        b   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php  UY  Zv      W   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php	  UY	  Y4      Y   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php  UY   ϶q      W   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php  UY  a
      \   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php  UY  ˱Զ      [   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php  UY  $p      X   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php  UY  =      c   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php7  UY7  {Hƶ      _   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/ResponseBodyVisitor.php  UY  V      V   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php  UY        d   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.phpf  UYf  }M      X   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/BodyVisitor.php3  UY3        Z   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php  UY  տ      X   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php  UY  貶      `   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.phpH  UYH  1      e   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php2  UY2  [      ^   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/StatusCodeVisitor.phpB  UYB  x@      W   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php  UY  
i      T   vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php  UY  Q      D   vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php	  UY	  Y      K   vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php  UY  R      N   vendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php  UY  ÖEk      J   vendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseClassInterface.php  UY  so      K   vendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseParserInterface.php  UY  w{      A   vendor/guzzle/guzzle/src/Guzzle/Service/ConfigLoaderInterface.php  UY  VQh      A   vendor/guzzle/guzzle/src/Guzzle/Service/Description/Operation.phpv=  UYv=        J   vendor/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php  UY  e       A   vendor/guzzle/guzzle/src/Guzzle/Service/Description/Parameter.phpa  UYa  [      G   vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php  UY  /lF      G   vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.phpj.  UYj.  Cc޶      J   vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php  UY  #Ŷ      S   vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php  UY  /S      P   vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionLoader.php	  UY	  %@      J   vendor/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php  UY  b      F   vendor/guzzle/guzzle/src/Guzzle/Service/Exception/CommandException.php   UY   կ      N   vendor/guzzle/guzzle/src/Guzzle/Service/Exception/CommandTransferException.php  UY  V϶      Q   vendor/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php   UY   ƣ      Y   vendor/guzzle/guzzle/src/Guzzle/Service/Exception/InconsistentClientTransferException.php  UY  y      L   vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php   UY   #N      M   vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ServiceBuilderException.php   UY   Nζ      N   vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ServiceNotFoundException.phpn   UYn   Sh      I   vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ValidationException.php  UY  bd      T   vendor/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php  UY  `      U   vendor/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php  UY  쾒      O   vendor/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php  UY        :   vendor/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php  UY  BpQ      E   vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php\  UY\  K˶      Q   vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.phpS  UYS  
֜      Q   vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php  UY  q      U   vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php  UY  癤Ѷ      N   vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorInterface.php  UY  2P      B   vendor/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php%  UY%  -      1   vendor/guzzle/guzzle/src/Guzzle/Stream/Stream.php  UY  |      :   vendor/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php8  UY8  1      H   vendor/guzzle/guzzle/src/Guzzle/Stream/StreamRequestFactoryInterface.php[  UY[  4      0   vendor/johnstevenson/json-works/src/Document.php(  UY(  qÄ      :   vendor/johnstevenson/json-works/src/Schema/Constraints.php3  UY3        4   vendor/johnstevenson/json-works/src/Schema/Model.php)
  UY)
  e(č      B   vendor/johnstevenson/json-works/src/Schema/ValidationException.phpc   UYc   Uɶ      8   vendor/johnstevenson/json-works/src/Schema/Validator.php  UY  1Ͷ      -   vendor/johnstevenson/json-works/src/Utils.php%  UY%  >      2   vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php  UY  \U      B   vendor/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.php`   UY`   .ڝ      R   vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php  UY  xkж      W   vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.phpe  UYe  +ֶ      M   vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.phpy  UYy  -      7   vendor/myclabs/deep-copy/src/DeepCopy/Filter/Filter.phpS  UYS  ksk      ;   vendor/myclabs/deep-copy/src/DeepCopy/Filter/KeepFilter.php   UY   ߝ      >   vendor/myclabs/deep-copy/src/DeepCopy/Filter/ReplaceFilter.php  UY  ̎|      >   vendor/myclabs/deep-copy/src/DeepCopy/Filter/SetNullFilter.php  UY  M䧶      O   vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.phpX  UYX  bO      9   vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Matcher.php   UY   ((      A   vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyMatcher.php  UY  uօ      E   vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyNameMatcher.php  UY  y㻞      E   vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyTypeMatcher.phpz  UYz  =#޶      E   vendor/myclabs/deep-copy/src/DeepCopy/Reflection/ReflectionHelper.php9  UY9  jZ      B   vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ReplaceFilter.php  UY  ;If      F   vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ShallowCopyFilter.php   UY   ت      L   vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php   UY   )      ?   vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/TypeFilter.php   UY   94      A   vendor/myclabs/deep-copy/src/DeepCopy/TypeMatcher/TypeMatcher.php  UY  ʨ          vendor/phing/phing/bin/phing.php  UY  `S%      6   vendor/phing/phing/build/BuildPhingPEARPackageTask.php~-  UY~-  P      '   vendor/phing/phing/build/phing-stub.php   UY   @      /   vendor/phing/phing/classes/phing/BuildEvent.php  UY  >X      3   vendor/phing/phing/classes/phing/BuildException.phpa  UYa  `*      2   vendor/phing/phing/classes/phing/BuildListener.php7  UY7  =Ѷ      0   vendor/phing/phing/classes/phing/BuildLogger.phpb  UYb  GEc      :   vendor/phing/phing/classes/phing/BuildTimeoutException.php  UY  qm      ;   vendor/phing/phing/classes/phing/ConfigurationException.php	  UY	  t[ٶ      E   vendor/phing/phing/classes/phing/contrib/DocBlox/Parallel/Manager.php$  UY$  'J0      D   vendor/phing/phing/classes/phing/contrib/DocBlox/Parallel/Worker.php  UY        H   vendor/phing/phing/classes/phing/contrib/DocBlox/Parallel/WorkerPipe.php~  UY~  ()      0   vendor/phing/phing/classes/phing/Diagnostics.php  UY        8   vendor/phing/phing/classes/phing/ExitStatusException.php|  UY|  ]V      =   vendor/phing/phing/classes/phing/filters/BaseFilterReader.php  UY  lQ      B   vendor/phing/phing/classes/phing/filters/BaseParamFilterReader.phpw	  UYw	  F-      <   vendor/phing/phing/classes/phing/filters/ChainableReader.phpF  UYF  pY[      9   vendor/phing/phing/classes/phing/filters/ConcatFilter.phpM  UYM  z'      :   vendor/phing/phing/classes/phing/filters/EscapeUnicode.php  UY  j      =   vendor/phing/phing/classes/phing/filters/ExpandProperties.php`  UY`  9      7   vendor/phing/phing/classes/phing/filters/HeadFilter.php  UY  ~^      8   vendor/phing/phing/classes/phing/filters/IconvFilter.phpY  UYY  $ݶ      9   vendor/phing/phing/classes/phing/filters/LineContains.php   UY   sT      ?   vendor/phing/phing/classes/phing/filters/LineContainsRegexp.phpM  UYM  e˶      =   vendor/phing/phing/classes/phing/filters/PhpArrayMapLines.php  UY  |Q      8   vendor/phing/phing/classes/phing/filters/PrefixLines.phpG  UYG  &2w      :   vendor/phing/phing/classes/phing/filters/ReplaceRegexp.php1  UY1  (      :   vendor/phing/phing/classes/phing/filters/ReplaceTokens.php(7  UY(7  1P      B   vendor/phing/phing/classes/phing/filters/ReplaceTokensWithFile.php/  UY/  !)(      7   vendor/phing/phing/classes/phing/filters/SortFilter.php  UY  O      <   vendor/phing/phing/classes/phing/filters/StripLineBreaks.php  UY  i      >   vendor/phing/phing/classes/phing/filters/StripLineComments.php  UY  Ù      =   vendor/phing/phing/classes/phing/filters/StripPhpComments.php  UY        <   vendor/phing/phing/classes/phing/filters/StripWhitespace.php  UY  <˶      8   vendor/phing/phing/classes/phing/filters/SuffixLines.phpx  UYx  YM      8   vendor/phing/phing/classes/phing/filters/TabToSpaces.php  UY  U\      7   vendor/phing/phing/classes/phing/filters/TailFilter.php  UY  Ԍ      7   vendor/phing/phing/classes/phing/filters/TidyFilter.php  UY        =   vendor/phing/phing/classes/phing/filters/TranslateGettext.php&  UY&  ~      C   vendor/phing/phing/classes/phing/filters/util/ChainReaderHelper.php  UY  @ʶ      D   vendor/phing/phing/classes/phing/filters/util/IniFileTokenReader.php;  UY;        ;   vendor/phing/phing/classes/phing/filters/XincludeFilter.php  UY  x/;      7   vendor/phing/phing/classes/phing/filters/XsltFilter.php,  UY,  xE      >   vendor/phing/phing/classes/phing/input/DefaultInputHandler.php  UY  Z4      7   vendor/phing/phing/classes/phing/input/InputHandler.php  UY  h䏶      7   vendor/phing/phing/classes/phing/input/InputRequest.php>  UY>  #3      E   vendor/phing/phing/classes/phing/input/MultipleChoiceInputRequest.php  UY  8N      <   vendor/phing/phing/classes/phing/input/YesNoInputRequest.php  UY  !      8   vendor/phing/phing/classes/phing/IntrospectionHelper.php_  UY_  (r      0   vendor/phing/phing/classes/phing/lib/Capsule.php  UY  gDk      =   vendor/phing/phing/classes/phing/listener/AnsiColorLogger.php  UY  *~      ;   vendor/phing/phing/classes/phing/listener/DefaultLogger.phpY(  UYY(  0      =   vendor/phing/phing/classes/phing/listener/HtmlColorLogger.php  UY  %      8   vendor/phing/phing/classes/phing/listener/JsonLogger.php  UY  ;      8   vendor/phing/phing/classes/phing/listener/MailLogger.php  UY  О      <   vendor/phing/phing/classes/phing/listener/NoBannerLogger.php\	  UY\	  Ile      =   vendor/phing/phing/classes/phing/listener/PearLogListener.phpn  UYn  0Ghy      ;   vendor/phing/phing/classes/phing/listener/ProfileLogger.php  UY  3a3      :   vendor/phing/phing/classes/phing/listener/SilentLogger.php9  UY9  ,Ѷ      G   vendor/phing/phing/classes/phing/listener/StreamRequiredBuildLogger.phpM  UYM  )$      :   vendor/phing/phing/classes/phing/listener/TargetLogger.phpZ  UYZ  Å      ?   vendor/phing/phing/classes/phing/listener/TimestampedLogger.php%  UY%  H"      7   vendor/phing/phing/classes/phing/listener/XmlLogger.php&6  UY&6  >      :   vendor/phing/phing/classes/phing/mappers/ChainedMapper.php  UY  9      <   vendor/phing/phing/classes/phing/mappers/CompositeMapper.php  UY  G      <   vendor/phing/phing/classes/phing/mappers/ContainerMapper.phpY  UYY  !{S      :   vendor/phing/phing/classes/phing/mappers/CutDirsMapper.php5	  UY5	  N(0      ;   vendor/phing/phing/classes/phing/mappers/FileNameMapper.php<  UY<        =   vendor/phing/phing/classes/phing/mappers/FirstMatchMapper.php  UY  3z      :   vendor/phing/phing/classes/phing/mappers/FlattenMapper.php  UY  A'[      7   vendor/phing/phing/classes/phing/mappers/GlobMapper.php  UY  #      ;   vendor/phing/phing/classes/phing/mappers/IdentityMapper.php  UY  ֛`      8   vendor/phing/phing/classes/phing/mappers/MergeMapper.phpI	  UYI	  H      9   vendor/phing/phing/classes/phing/mappers/RegexpMapper.php~  UY~  f^#      ;   vendor/phing/phing/classes/phing/parser/AbstractHandler.php;  UY;  !k      =   vendor/phing/phing/classes/phing/parser/AbstractSAXParser.php  UY  ]Ŷ      >   vendor/phing/phing/classes/phing/parser/CustomChildCreator.php#  UY#  6@      ;   vendor/phing/phing/classes/phing/parser/DataTypeHandler.php  UY  ~m      :   vendor/phing/phing/classes/phing/parser/ElementHandler.phpF  UYF  Lv      ?   vendor/phing/phing/classes/phing/parser/ExpatParseException.php4  UY4  ܍1      7   vendor/phing/phing/classes/phing/parser/ExpatParser.php5  UY5  |]      4   vendor/phing/phing/classes/phing/parser/Location.php	  UY	  3      ;   vendor/phing/phing/classes/phing/parser/PhingXMLContext.phpF  UYF  T#      ?   vendor/phing/phing/classes/phing/parser/ProjectConfigurator.phpA8  UYA8  D      :   vendor/phing/phing/classes/phing/parser/ProjectHandler.php`  UY`  le      7   vendor/phing/phing/classes/phing/parser/RootHandler.php  UY  !j      9   vendor/phing/phing/classes/phing/parser/TargetHandler.php  UY  Yes      *   vendor/phing/phing/classes/phing/Phing.php(  UY(  <ض      ,   vendor/phing/phing/classes/phing/Project.php  UY  >~ȶ      5   vendor/phing/phing/classes/phing/ProjectComponent.php"	  UY"	  Z؞      8   vendor/phing/phing/classes/phing/RuntimeConfigurable.php  UY  h(      5   vendor/phing/phing/classes/phing/SubBuildListener.phpk	  UYk	  B7      =   vendor/phing/phing/classes/phing/system/io/BufferedReader.phpp  UYp  :ܶ      =   vendor/phing/phing/classes/phing/system/io/BufferedWriter.php	  UY	  ?n      <   vendor/phing/phing/classes/phing/system/io/ConsoleReader.php	  UY	  e"      >   vendor/phing/phing/classes/phing/system/io/FileInputStream.php  UY  bu      ?   vendor/phing/phing/classes/phing/system/io/FileOutputStream.php
  UY
  \[      @   vendor/phing/phing/classes/phing/system/io/FileParserFactory.php`  UY`  ?3      I   vendor/phing/phing/classes/phing/system/io/FileParserFactoryInterface.php  UY  ]      B   vendor/phing/phing/classes/phing/system/io/FileParserInterface.php  UY        9   vendor/phing/phing/classes/phing/system/io/FileReader.php  UY  U      9   vendor/phing/phing/classes/phing/system/io/FileSystem.phpk  UYk  sv
      9   vendor/phing/phing/classes/phing/system/io/FileWriter.phpI  UYI        ;   vendor/phing/phing/classes/phing/system/io/FilterReader.php  UY  {$      <   vendor/phing/phing/classes/phing/system/io/IniFileParser.php  UY  H
2      :   vendor/phing/phing/classes/phing/system/io/InputStream.phpN  UYN  x(      @   vendor/phing/phing/classes/phing/system/io/InputStreamReader.php  UY  9_      :   vendor/phing/phing/classes/phing/system/io/IOException.php  UY  g>d      ;   vendor/phing/phing/classes/phing/system/io/OutputStream.php  UY  |o      A   vendor/phing/phing/classes/phing/system/io/OutputStreamWriter.php	  UY	  <      8   vendor/phing/phing/classes/phing/system/io/PhingFile.php  UY  L      :   vendor/phing/phing/classes/phing/system/io/PrintStream.php	  UY	  ݶ      5   vendor/phing/phing/classes/phing/system/io/Reader.php
  UY
  un&      ;   vendor/phing/phing/classes/phing/system/io/StringReader.php	  UY	  P      =   vendor/phing/phing/classes/phing/system/io/UnixFileSystem.php(  UY(  µ<¶      >   vendor/phing/phing/classes/phing/system/io/Win32FileSystem.phpF  UYF  r      >   vendor/phing/phing/classes/phing/system/io/WinNTFileSystem.php  UY  t      5   vendor/phing/phing/classes/phing/system/io/Writer.php  UY  	      =   vendor/phing/phing/classes/phing/system/io/YamlFileParser.phpF  UYF  auN      :   vendor/phing/phing/classes/phing/system/lang/Character.php  UY  _Ҧ      <   vendor/phing/phing/classes/phing/system/lang/EventObject.php  UY  F      F   vendor/phing/phing/classes/phing/system/lang/FileNotFoundException.php_  UY_  
      E   vendor/phing/phing/classes/phing/system/lang/NullPointerException.php\  UY\  Yɜ+      B   vendor/phing/phing/classes/phing/system/lang/SecurityException.phpY  UYY  g      ;   vendor/phing/phing/classes/phing/system/util/Properties.php:$  UY:$  ƶ      9   vendor/phing/phing/classes/phing/system/util/Register.php  UY  R      6   vendor/phing/phing/classes/phing/system/util/Timer.php
  UY
  nIl      +   vendor/phing/phing/classes/phing/Target.php&-  UY&-  W޶      )   vendor/phing/phing/classes/phing/Task.php  UY  s ƶ      0   vendor/phing/phing/classes/phing/TaskAdapter.php  UY  C?Q      2   vendor/phing/phing/classes/phing/TaskContainer.php  UY  7F.      @   vendor/phing/phing/classes/phing/tasks/ext/apigen/ApiGenTask.php+  UY+  QҶ      =   vendor/phing/phing/classes/phing/tasks/ext/AutoloaderTask.php	  UY	  a2&      :   vendor/phing/phing/classes/phing/tasks/ext/CapsuleTask.php<  UY<  PP\      ;   vendor/phing/phing/classes/phing/tasks/ext/ComposerTask.php  UY  9      F   vendor/phing/phing/classes/phing/tasks/ext/coverage/CoverageMerger.php(  UY(  mG      J   vendor/phing/phing/classes/phing/tasks/ext/coverage/CoverageMergerTask.php  UY  ^{܍      J   vendor/phing/phing/classes/phing/tasks/ext/coverage/CoverageReportTask.php
M  UY
M  hmW      Q   vendor/phing/phing/classes/phing/tasks/ext/coverage/CoverageReportTransformer.php8  UY8        I   vendor/phing/phing/classes/phing/tasks/ext/coverage/CoverageSetupTask.php  UY  ݨx      M   vendor/phing/phing/classes/phing/tasks/ext/coverage/CoverageThresholdTask.php:  UY:  9Qo      G   vendor/phing/phing/classes/phing/tasks/ext/creole/CreoleSQLExecTask.phpRJ  UYRJ  $+      @   vendor/phing/phing/classes/phing/tasks/ext/creole/CreoleTask.phpH  UYH   ֶ      D   vendor/phing/phing/classes/phing/tasks/ext/dbdeploy/DbDeployTask.php5  UY5  CUҋ      B   vendor/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntax.php  UY  W      I   vendor/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxFactory.php	  UY	  =;      G   vendor/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxMsSql.php  UY  Pn      G   vendor/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxMysql.php  UY  QPL      H   vendor/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxOracle.phpd  UYd  gB      G   vendor/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxPgSQL.phps  UYs  v      H   vendor/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxSQLite.php  UY  Cc      C   vendor/phing/phing/classes/phing/tasks/ext/ExportPropertiesTask.phpI  UYI  v      >   vendor/phing/phing/classes/phing/tasks/ext/ExtractBaseTask.php  UY  pC      ;   vendor/phing/phing/classes/phing/tasks/ext/FileHashTask.phpo  UYo  P6      ;   vendor/phing/phing/classes/phing/tasks/ext/FileSizeTask.php_  UY_  O۶      ;   vendor/phing/phing/classes/phing/tasks/ext/FileSyncTask.php1?  UY1?  /i      <   vendor/phing/phing/classes/phing/tasks/ext/FtpDeployTask.phpg:  UYg:        >   vendor/phing/phing/classes/phing/tasks/ext/git/GitBaseTask.php  UY  9      @   vendor/phing/phing/classes/phing/tasks/ext/git/GitBranchTask.php  UY  ΂9      B   vendor/phing/phing/classes/phing/tasks/ext/git/GitCheckoutTask.php  UY        ?   vendor/phing/phing/classes/phing/tasks/ext/git/GitCloneTask.php  UY  9      @   vendor/phing/phing/classes/phing/tasks/ext/git/GitCommitTask.phpa  UYa        B   vendor/phing/phing/classes/phing/tasks/ext/git/GitDescribeTask.php  UY  &      ?   vendor/phing/phing/classes/phing/tasks/ext/git/GitFetchTask.php  UY  I0      <   vendor/phing/phing/classes/phing/tasks/ext/git/GitGcTask.phpm  UYm  X+      >   vendor/phing/phing/classes/phing/tasks/ext/git/GitInitTask.php	  UY	  jbq      =   vendor/phing/phing/classes/phing/tasks/ext/git/GitLogTask.php  UY  _ބ      ?   vendor/phing/phing/classes/phing/tasks/ext/git/GitMergeTask.php  UY  ֶ      >   vendor/phing/phing/classes/phing/tasks/ext/git/GitPullTask.php-'  UY-'  b"      >   vendor/phing/phing/classes/phing/tasks/ext/git/GitPushTask.php  UY  ;      =   vendor/phing/phing/classes/phing/tasks/ext/git/GitTagTask.php)  UY)        >   vendor/phing/phing/classes/phing/tasks/ext/GrowlNotifyTask.phpq<  UYq<  c      ;   vendor/phing/phing/classes/phing/tasks/ext/hg/HgAddTask.php  UY  w;v      ?   vendor/phing/phing/classes/phing/tasks/ext/hg/HgArchiveTask.php  UY  45Z3      <   vendor/phing/phing/classes/phing/tasks/ext/hg/HgBaseTask.php(  UY(  >S      =   vendor/phing/phing/classes/phing/tasks/ext/hg/HgCloneTask.php%  UY%  i3      >   vendor/phing/phing/classes/phing/tasks/ext/hg/HgCommitTask.php  UY  y      <   vendor/phing/phing/classes/phing/tasks/ext/hg/HgInitTask.php  UY  4      ;   vendor/phing/phing/classes/phing/tasks/ext/hg/HgLogTask.php  UY  !e      <   vendor/phing/phing/classes/phing/tasks/ext/hg/HgPullTask.phpL  UYL        <   vendor/phing/phing/classes/phing/tasks/ext/hg/HgPushTask.php	  UY	  	*&      >   vendor/phing/phing/classes/phing/tasks/ext/hg/HgRevertTask.php  UY   sԻ      ;   vendor/phing/phing/classes/phing/tasks/ext/hg/HgTagTask.phpN  UYN  +m      >   vendor/phing/phing/classes/phing/tasks/ext/hg/HgUpdateTask.php^  UY^  Oy      :   vendor/phing/phing/classes/phing/tasks/ext/HttpGetTask.php  UY        >   vendor/phing/phing/classes/phing/tasks/ext/HttpRequestTask.php  UY  %"Z      7   vendor/phing/phing/classes/phing/tasks/ext/HttpTask.php6  UY6  rt      D   vendor/phing/phing/classes/phing/tasks/ext/inifile/IniFileConfig.php  UY  
w      D   vendor/phing/phing/classes/phing/tasks/ext/inifile/IniFileRemove.php%  UY%  c      A   vendor/phing/phing/classes/phing/tasks/ext/inifile/IniFileSet.php9	  UY9	  O>      B   vendor/phing/phing/classes/phing/tasks/ext/inifile/IniFileTask.php$  UY$  ƛ      E   vendor/phing/phing/classes/phing/tasks/ext/ioncube/IoncubeComment.php  UY  ׶      I   vendor/phing/phing/classes/phing/tasks/ext/ioncube/IoncubeEncoderTask.php<  UY<  p8      I   vendor/phing/phing/classes/phing/tasks/ext/ioncube/IoncubeLicenseTask.php  UY  )co      9   vendor/phing/phing/classes/phing/tasks/ext/JsHintTask.php  UY  !      :   vendor/phing/phing/classes/phing/tasks/ext/JslLintTask.php;+  UY;+  0      >   vendor/phing/phing/classes/phing/tasks/ext/jsmin/JsMinTask.php  UY  .L      N   vendor/phing/phing/classes/phing/tasks/ext/liquibase/AbstractLiquibaseTask.php+  UY+  麐      O   vendor/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseChangeLogTask.php_  UY_  cd      K   vendor/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseDbDocTask.php  UY  7      J   vendor/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseDiffTask.php  UY  2✍      N   vendor/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseRollbackTask.php  UY  \      I   vendor/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseTagTask.phph  UYh  w      F   vendor/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseTask.php.  UY.  2      L   vendor/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseUpdateTask.phpu  UYu  >      7   vendor/phing/phing/classes/phing/tasks/ext/MailTask.php  UY        ;   vendor/phing/phing/classes/phing/tasks/ext/ManifestTask.php|#  UY|#  k      =   vendor/phing/phing/classes/phing/tasks/ext/NotifySendTask.php  UY  2      @   vendor/phing/phing/classes/phing/tasks/ext/PackageAsPathTask.php  UY  {      ;   vendor/phing/phing/classes/phing/tasks/ext/ParallelTask.php]  UY]  VL      8   vendor/phing/phing/classes/phing/tasks/ext/PatchTask.php+  UY+  Զ      O   vendor/phing/phing/classes/phing/tasks/ext/pdepend/PhpDependAnalyzerElement.php^
  UY^
   n      M   vendor/phing/phing/classes/phing/tasks/ext/pdepend/PhpDependLoggerElement.php1
  UY1
  9      D   vendor/phing/phing/classes/phing/tasks/ext/pdepend/PhpDependTask.php<  UY<  ͙      J   vendor/phing/phing/classes/phing/tasks/ext/pdo/DefaultPDOQuerySplitter.phpX  UYX  ~      H   vendor/phing/phing/classes/phing/tasks/ext/pdo/DummyPDOQuerySplitter.php	  UY	  1ݶ      C   vendor/phing/phing/classes/phing/tasks/ext/pdo/PDOQuerySplitter.php  UY  .      E   vendor/phing/phing/classes/phing/tasks/ext/pdo/PDOResultFormatter.php  UY  S:      M   vendor/phing/phing/classes/phing/tasks/ext/pdo/PDOSQLExecFormatterElement.php!  UY!  ;      A   vendor/phing/phing/classes/phing/tasks/ext/pdo/PDOSQLExecTask.php*K  UY*K  M
VO      :   vendor/phing/phing/classes/phing/tasks/ext/pdo/PDOTask.php  UY  q      H   vendor/phing/phing/classes/phing/tasks/ext/pdo/PgsqlPDOQuerySplitter.php(  UY(  ~      J   vendor/phing/phing/classes/phing/tasks/ext/pdo/PlainPDOResultFormatter.php  UY  2      H   vendor/phing/phing/classes/phing/tasks/ext/pdo/XMLPDOResultFormatter.php  UY  k=p      B   vendor/phing/phing/classes/phing/tasks/ext/pearpackage/Fileset.php  UY  xzO      ?   vendor/phing/phing/classes/phing/tasks/ext/PearPackage2Task.phpN+  UYN+  `׶      >   vendor/phing/phing/classes/phing/tasks/ext/PearPackageTask.php9  UY9  YD      @   vendor/phing/phing/classes/phing/tasks/ext/phar/PharDataTask.php#  UY#  ^      @   vendor/phing/phing/classes/phing/tasks/ext/phar/PharMetadata.php  UY  rAF      G   vendor/phing/phing/classes/phing/tasks/ext/phar/PharMetadataElement.php?  UY?  O      C   vendor/phing/phing/classes/phing/tasks/ext/phar/PharPackageTask.phpQ.  UYQ.  T8      A   vendor/phing/phing/classes/phing/tasks/ext/phk/PhkPackageTask.php5  UY5  ބ      F   vendor/phing/phing/classes/phing/tasks/ext/phk/PhkPackageWebAccess.php  UY  rT߶      J   vendor/phing/phing/classes/phing/tasks/ext/phk/PhkPackageWebAccessPath.php  UY  y됣      A   vendor/phing/phing/classes/phing/tasks/ext/PhpCodeSnifferTask.php]  UY]  cn      \   vendor/phing/phing/classes/phing/tasks/ext/phpcpd/formatter/DefaultPHPCPDResultFormatter.php  UY  `      U   vendor/phing/phing/classes/phing/tasks/ext/phpcpd/formatter/PHPCPDResultFormatter.phpc  UYc        X   vendor/phing/phing/classes/phing/tasks/ext/phpcpd/formatter/PMDPHPCPDResultFormatter.php  UY  *϶      L   vendor/phing/phing/classes/phing/tasks/ext/phpcpd/PHPCPDFormatterElement.php  UY  -'3      @   vendor/phing/phing/classes/phing/tasks/ext/phpcpd/PHPCPDTask.php&  UY&  =}      O   vendor/phing/phing/classes/phing/tasks/ext/phpcs/PhpCodeSnifferTask_Wrapper.phpc  UYc  N      Q   vendor/phing/phing/classes/phing/tasks/ext/phpcs/Reports_PhingRemoveFromCache.php  UY  PMG      T   vendor/phing/phing/classes/phing/tasks/ext/phpdoc/PhingPhpDocumentorErrorTracker.php  UY  }V      M   vendor/phing/phing/classes/phing/tasks/ext/phpdoc/PhingPhpDocumentorSetup.php"  UY"  Ѷ      H   vendor/phing/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentor2Task.phpH  UYH  hY\~      K   vendor/phing/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentor2Wrapper.phpI  UYI  <0      O   vendor/phing/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentorExternalTask.php  UY  |޶      G   vendor/phing/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentorTask.php5  UY5        :   vendor/phing/phing/classes/phing/tasks/ext/PhpLintTask.phps"  UYs"  o      M   vendor/phing/phing/classes/phing/tasks/ext/phploc/AbstractPHPLocFormatter.php
  UY
  f      H   vendor/phing/phing/classes/phing/tasks/ext/phploc/PHPLocCSVFormatter.php;  UY;  R      L   vendor/phing/phing/classes/phing/tasks/ext/phploc/PHPLocFormatterElement.php
  UY
  _+E      L   vendor/phing/phing/classes/phing/tasks/ext/phploc/PHPLocFormatterFactory.php
  UY
  :+      @   vendor/phing/phing/classes/phing/tasks/ext/phploc/PHPLocTask.php&  UY&  眶      I   vendor/phing/phing/classes/phing/tasks/ext/phploc/PHPLocTextFormatter.php  UY  &"      H   vendor/phing/phing/classes/phing/tasks/ext/phploc/PHPLocXMLFormatter.php3  UY3  *8      J   vendor/phing/phing/classes/phing/tasks/ext/phpmd/PHPMDFormatterElement.php  UY  6!̴      Q   vendor/phing/phing/classes/phing/tasks/ext/phpmd/PHPMDRendererRemoveFromCache.php  UY  \}j      >   vendor/phing/phing/classes/phing/tasks/ext/phpmd/PHPMDTask.php})  UY})  RL      @   vendor/phing/phing/classes/phing/tasks/ext/phpunit/BatchTest.php>  UY>  $Ձ      ]   vendor/phing/phing/classes/phing/tasks/ext/phpunit/formatter/CloverPHPUnitResultFormatter.php^  UY^  ̻^      ]   vendor/phing/phing/classes/phing/tasks/ext/phpunit/formatter/Crap4jPHPUnitResultFormatter.phpV  UYV  !ͨz      W   vendor/phing/phing/classes/phing/tasks/ext/phpunit/formatter/PHPUnitResultFormatter.php  UY  gf      \   vendor/phing/phing/classes/phing/tasks/ext/phpunit/formatter/PlainPHPUnitResultFormatter.phpM  UYM  O      ^   vendor/phing/phing/classes/phing/tasks/ext/phpunit/formatter/SummaryPHPUnitResultFormatter.php  UY  <      Z   vendor/phing/phing/classes/phing/tasks/ext/phpunit/formatter/XMLPHPUnitResultFormatter.php  UY  ~      G   vendor/phing/phing/classes/phing/tasks/ext/phpunit/FormatterElement.phpG  UYG  h      H   vendor/phing/phing/classes/phing/tasks/ext/phpunit/PHPUnitReportTask.php   UY   ^      B   vendor/phing/phing/classes/phing/tasks/ext/phpunit/PHPUnitTask.phpGC  UYGC  Qж      H   vendor/phing/phing/classes/phing/tasks/ext/phpunit/PHPUnitTestRunner.phpN,  UYN,  H      B   vendor/phing/phing/classes/phing/tasks/ext/phpunit/PHPUnitUtil.php  UY  hm忶      R   vendor/phing/phing/classes/phing/tasks/ext/property/AbstractPropertySetterTask.phpt  UYt  xk      E   vendor/phing/phing/classes/phing/tasks/ext/property/PathToFileSet.php  UY  mu      A   vendor/phing/phing/classes/phing/tasks/ext/property/RegexTask.php  UY  xPǶ      @   vendor/phing/phing/classes/phing/tasks/ext/ReplaceRegexpTask.php  UY  #tk      6   vendor/phing/phing/classes/phing/tasks/ext/rSTTask.php_/  UY_/  M6      7   vendor/phing/phing/classes/phing/tasks/ext/SassTask.php{  UY{  f       J   vendor/phing/phing/classes/phing/tasks/ext/Service/Amazon/S3/S3GetTask.php  UY  @      J   vendor/phing/phing/classes/phing/tasks/ext/Service/Amazon/S3/S3PutTask.php+  UY+  Fp      @   vendor/phing/phing/classes/phing/tasks/ext/Service/Amazon/S3.php  UY  ,      =   vendor/phing/phing/classes/phing/tasks/ext/Service/Amazon.phpu  UYu  V"_      X   vendor/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestCountResultFormatter.php  UY  BA      X   vendor/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestDebugResultFormatter.php  UY  Y-      T   vendor/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestFormatterElement.php_
  UY_
  tn      X   vendor/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestPlainResultFormatter.phpn  UYn  HE      S   vendor/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestResultFormatter.phpp  UYp  .      Z   vendor/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestSummaryResultFormatter.php  UY  9      H   vendor/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestTask.php  UY  GN@      V   vendor/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestXmlResultFormatter.php  UY  hP      9   vendor/phing/phing/classes/phing/tasks/ext/SmartyTask.phpM  UYM  ޏ      Q   vendor/phing/phing/classes/phing/tasks/ext/sonar/SonarConfigurationFileParser.php  UY  0      B   vendor/phing/phing/classes/phing/tasks/ext/sonar/SonarProperty.php  UY  ϶      >   vendor/phing/phing/classes/phing/tasks/ext/sonar/SonarTask.phpI1  UYI1  'Ѷ      :   vendor/phing/phing/classes/phing/tasks/ext/ssh/ScpTask.phpZ3  UYZ3  ;1+      L   vendor/phing/phing/classes/phing/tasks/ext/ssh/Ssh2MethodConnectionParam.php
  UY
        B   vendor/phing/phing/classes/phing/tasks/ext/ssh/Ssh2MethodParam.php\  UY\  @-	      :   vendor/phing/phing/classes/phing/tasks/ext/ssh/SshTask.php3   UY3   0h      <   vendor/phing/phing/classes/phing/tasks/ext/StopwatchTask.php  UY  UD      >   vendor/phing/phing/classes/phing/tasks/ext/svn/SvnBaseTask.php%  UY%  %P      B   vendor/phing/phing/classes/phing/tasks/ext/svn/SvnCheckoutTask.php  UY  o      @   vendor/phing/phing/classes/phing/tasks/ext/svn/SvnCommitTask.php`  UY`  Hb      >   vendor/phing/phing/classes/phing/tasks/ext/svn/SvnCopyTask.php  UY  +      @   vendor/phing/phing/classes/phing/tasks/ext/svn/SvnExportTask.php}  UY}  x2      >   vendor/phing/phing/classes/phing/tasks/ext/svn/SvnInfoTask.php2  UY2  +!      F   vendor/phing/phing/classes/phing/tasks/ext/svn/SvnLastRevisionTask.phpk  UYk         >   vendor/phing/phing/classes/phing/tasks/ext/svn/SvnListTask.phpd  UYd  f      =   vendor/phing/phing/classes/phing/tasks/ext/svn/SvnLogTask.phpB  UYB  Svݶ      @   vendor/phing/phing/classes/phing/tasks/ext/svn/SvnSwitchTask.php>	  UY>	  0A߶      @   vendor/phing/phing/classes/phing/tasks/ext/svn/SvnUpdateTask.phpz  UYz  b Ͷ      A   vendor/phing/phing/classes/phing/tasks/ext/SymfonyConsole/Arg.php  UY  }      P   vendor/phing/phing/classes/phing/tasks/ext/SymfonyConsole/SymfonyConsoleTask.php  UY  t      :   vendor/phing/phing/classes/phing/tasks/ext/SymlinkTask.php  UY  Z&៶      6   vendor/phing/phing/classes/phing/tasks/ext/TarTask.php!<  UY!<        8   vendor/phing/phing/classes/phing/tasks/ext/ThrowTask.php  UY  m      8   vendor/phing/phing/classes/phing/tasks/ext/UntarTask.php  UY  -ζ      8   vendor/phing/phing/classes/phing/tasks/ext/UnzipTask.php
  UY
  I[      :   vendor/phing/phing/classes/phing/tasks/ext/VersionTask.php  UY  Z      >   vendor/phing/phing/classes/phing/tasks/ext/WikiPublishTask.php$)  UY$)  zꣶ      :   vendor/phing/phing/classes/phing/tasks/ext/XmlLintTask.phpu  UYu  fض      >   vendor/phing/phing/classes/phing/tasks/ext/XmlPropertyTask.phpd   UYd   ͓      C   vendor/phing/phing/classes/phing/tasks/ext/ZendCodeAnalyzerTask.php   UY   	      L   vendor/phing/phing/classes/phing/tasks/ext/zendguard/ZendGuardEncodeTask.php<  UY<  h      M   vendor/phing/phing/classes/phing/tasks/ext/zendguard/ZendGuardLicenseTask.phpB  UYB  y      T   vendor/phing/phing/classes/phing/tasks/ext/zendserverdeploymenttool/zsdtBaseTask.php  UY        T   vendor/phing/phing/classes/phing/tasks/ext/zendserverdeploymenttool/zsdtPackTask.php  UY  6D*      X   vendor/phing/phing/classes/phing/tasks/ext/zendserverdeploymenttool/zsdtValidateTask.php  UY  m#d      6   vendor/phing/phing/classes/phing/tasks/ext/ZipTask.php*  UY*  oV      ;   vendor/phing/phing/classes/phing/tasks/system/AdhocTask.phpA  UYA  S      B   vendor/phing/phing/classes/phing/tasks/system/AdhocTaskdefTask.phpX  UYX  ̌      B   vendor/phing/phing/classes/phing/tasks/system/AdhocTypedefTask.php

  UY

        H   vendor/phing/phing/classes/phing/tasks/system/AppendTask/TextElement.php  UY  ⳶      <   vendor/phing/phing/classes/phing/tasks/system/AppendTask.php=  UY=  :杶      ;   vendor/phing/phing/classes/phing/tasks/system/ApplyTask.php_\  UY_\  )pn      <   vendor/phing/phing/classes/phing/tasks/system/AttribTask.php  UY  u-      ?   vendor/phing/phing/classes/phing/tasks/system/AvailableTask.phpj  UYj  t      :   vendor/phing/phing/classes/phing/tasks/system/Basename.phpH  UYH  yO      >   vendor/phing/phing/classes/phing/tasks/system/BlockForTask.phpW  UYW  CӶ      ;   vendor/phing/phing/classes/phing/tasks/system/ChmodTask.php  UY  +      ;   vendor/phing/phing/classes/phing/tasks/system/ChownTask.php  UY  }       H   vendor/phing/phing/classes/phing/tasks/system/condition/AndCondition.php  UY  'V      E   vendor/phing/phing/classes/phing/tasks/system/condition/Condition.php  UY        I   vendor/phing/phing/classes/phing/tasks/system/condition/ConditionBase.php(  UY(  !ߤ      M   vendor/phing/phing/classes/phing/tasks/system/condition/ContainsCondition.php	  UY	  tT;p      K   vendor/phing/phing/classes/phing/tasks/system/condition/EqualsCondition.php
  UY
  X;      F   vendor/phing/phing/classes/phing/tasks/system/condition/FilesMatch.php  UY  ʬ      Q   vendor/phing/phing/classes/phing/tasks/system/condition/HasFreeSpaceCondition.php  UY  Zg      I   vendor/phing/phing/classes/phing/tasks/system/condition/HttpCondition.php  UY  Х      E   vendor/phing/phing/classes/phing/tasks/system/condition/IsFailure.php]  UY]  t      L   vendor/phing/phing/classes/phing/tasks/system/condition/IsFalseCondition.php  UY  ']      J   vendor/phing/phing/classes/phing/tasks/system/condition/IsFileSelected.php
  UY
        T   vendor/phing/phing/classes/phing/tasks/system/condition/IsPropertyFalseCondition.php  UY  M      S   vendor/phing/phing/classes/phing/tasks/system/condition/IsPropertyTrueCondition.php  UY        J   vendor/phing/phing/classes/phing/tasks/system/condition/IsSetCondition.php  UY  ؟y      K   vendor/phing/phing/classes/phing/tasks/system/condition/IsTrueCondition.php  UY  P9      C   vendor/phing/phing/classes/phing/tasks/system/condition/Matches.phpZ  UYZ  G      K   vendor/phing/phing/classes/phing/tasks/system/condition/NestedCondition.php$  UY$  /|}      H   vendor/phing/phing/classes/phing/tasks/system/condition/NotCondition.php  UY  2%      G   vendor/phing/phing/classes/phing/tasks/system/condition/OrCondition.php  UY  ^      G   vendor/phing/phing/classes/phing/tasks/system/condition/OsCondition.php
  UY
        H   vendor/phing/phing/classes/phing/tasks/system/condition/PhingVersion.php  UY  zw      T   vendor/phing/phing/classes/phing/tasks/system/condition/ReferenceExistsCondition.php  UY  IY      K   vendor/phing/phing/classes/phing/tasks/system/condition/SocketCondition.php  UY  oYR      S   vendor/phing/phing/classes/phing/tasks/system/condition/VersionCompareCondition.phpw  UYw  Ÿö      H   vendor/phing/phing/classes/phing/tasks/system/condition/XorCondition.php}  UY}  R      ?   vendor/phing/phing/classes/phing/tasks/system/ConditionTask.php  UY  {4_      :   vendor/phing/phing/classes/phing/tasks/system/CopyTask.php]M  UY]M  mҶ      =   vendor/phing/phing/classes/phing/tasks/system/CvsPassTask.php  UY  Q      9   vendor/phing/phing/classes/phing/tasks/system/CvsTask.php|;  UY|;  Ί      <   vendor/phing/phing/classes/phing/tasks/system/DeleteTask.php.  UY.  "      A   vendor/phing/phing/classes/phing/tasks/system/DiagnosticsTask.phpK  UYK        9   vendor/phing/phing/classes/phing/tasks/system/Dirname.php
  UY
  wö      @   vendor/phing/phing/classes/phing/tasks/system/EchoProperties.php1  UY1  mzc      :   vendor/phing/phing/classes/phing/tasks/system/EchoTask.php  UY        :   vendor/phing/phing/classes/phing/tasks/system/ExecTask.php7  UY7  <t      :   vendor/phing/phing/classes/phing/tasks/system/FailTask.phpY  UYY  C̶      =   vendor/phing/phing/classes/phing/tasks/system/ForeachTask.phpC+  UYC+  i;Ҷ      8   vendor/phing/phing/classes/phing/tasks/system/IfTask.php  UY  $v      <   vendor/phing/phing/classes/phing/tasks/system/ImportTask.phpp  UYp  x      A   vendor/phing/phing/classes/phing/tasks/system/IncludePathTask.php  UY  
      ;   vendor/phing/phing/classes/phing/tasks/system/InputTask.php  UY  ~ƶ      >   vendor/phing/phing/classes/phing/tasks/system/LoadFileTask.php  UY  N      >   vendor/phing/phing/classes/phing/tasks/system/MatchingTask.phpy,  UYy,  '4      ;   vendor/phing/phing/classes/phing/tasks/system/MkdirTask.php  UY  `w      :   vendor/phing/phing/classes/phing/tasks/system/MoveTask.php  UY  }      =   vendor/phing/phing/classes/phing/tasks/system/PathConvert.php41  UY41  l      ?   vendor/phing/phing/classes/phing/tasks/system/PhingCallTask.phpz  UYz  =      ;   vendor/phing/phing/classes/phing/tasks/system/PhingTask.phpzQ  UYzQ        =   vendor/phing/phing/classes/phing/tasks/system/PhpEvalTask.php  UY  lU,      D   vendor/phing/phing/classes/phing/tasks/system/PropertyPromptTask.php;  UY;  P      >   vendor/phing/phing/classes/phing/tasks/system/PropertyTask.phpF  UYF  TA      ?   vendor/phing/phing/classes/phing/tasks/system/RecorderEntry.php&  UY&  3      >   vendor/phing/phing/classes/phing/tasks/system/RecorderTask.php!  UY!  :h      ?   vendor/phing/phing/classes/phing/tasks/system/ReflexiveTask.phpU  UYU  6      A   vendor/phing/phing/classes/phing/tasks/system/ResolvePathTask.php`  UY`  uxZ      7   vendor/phing/phing/classes/phing/tasks/system/Retry.php  UY  " a      @   vendor/phing/phing/classes/phing/tasks/system/SequentialTask.php  UY  D0      ;   vendor/phing/phing/classes/phing/tasks/system/SleepTask.php  UY  0      <   vendor/phing/phing/classes/phing/tasks/system/SwitchTask.phpL  UYL  i      =   vendor/phing/phing/classes/phing/tasks/system/TaskdefTask.phpm  UYm  DNu      :   vendor/phing/phing/classes/phing/tasks/system/TempFile.phpP  UYP  j      ;   vendor/phing/phing/classes/phing/tasks/system/TouchTask.phpG  UYG  }      >   vendor/phing/phing/classes/phing/tasks/system/TruncateTask.php0  UY0  x{      >   vendor/phing/phing/classes/phing/tasks/system/TryCatchTask.php  UY  Y      <   vendor/phing/phing/classes/phing/tasks/system/TstampTask.php  UY  Y       =   vendor/phing/phing/classes/phing/tasks/system/TypedefTask.php`  UY`        >   vendor/phing/phing/classes/phing/tasks/system/UpToDateTask.php%  UY%  ,O      =   vendor/phing/phing/classes/phing/tasks/system/WaitForTask.php  UY  o      :   vendor/phing/phing/classes/phing/tasks/system/WarnTask.php  UY  Ȁ      :   vendor/phing/phing/classes/phing/tasks/system/XsltTask.php  UY  ض      :   vendor/phing/phing/classes/phing/types/AbstractFileSet.phpN  UYN        6   vendor/phing/phing/classes/phing/types/Commandline.php:  UY:  ~      3   vendor/phing/phing/classes/phing/types/DataType.php  UY         6   vendor/phing/phing/classes/phing/types/Description.php  UY  Bض      1   vendor/phing/phing/classes/phing/types/DirSet.php  UY  ¶      3   vendor/phing/phing/classes/phing/types/Excludes.php  UY  ͚      <   vendor/phing/phing/classes/phing/types/ExcludesNameEntry.php  UY  o      3   vendor/phing/phing/classes/phing/types/FileList.php  UY  -m      2   vendor/phing/phing/classes/phing/types/FileSet.php  UY  $ֶ      6   vendor/phing/phing/classes/phing/types/FilterChain.php!  UY!        :   vendor/phing/phing/classes/phing/types/IterableFileSet.php  UY        1   vendor/phing/phing/classes/phing/types/Mapper.phpC$  UYC$  i      4   vendor/phing/phing/classes/phing/types/Parameter.php?  UY?  o      :   vendor/phing/phing/classes/phing/types/Parameterizable.php?  UY?  %      /   vendor/phing/phing/classes/phing/types/Path.phpD@  UYD@  T:      5   vendor/phing/phing/classes/phing/types/PatternSet.php>  UY>  SY       =   vendor/phing/phing/classes/phing/types/PearPackageFileSet.php  UY  P_v      <   vendor/phing/phing/classes/phing/types/PhingFilterReader.php  UY  Ҷ      8   vendor/phing/phing/classes/phing/types/PropertyValue.php  UY  H      4   vendor/phing/phing/classes/phing/types/Reference.phpV  UYV  }      <   vendor/phing/phing/classes/phing/types/RegularExpression.phpg  UYg  e4      N   vendor/phing/phing/classes/phing/types/selectors/AbstractSelectorContainer.phpb)  UYb)  "d      @   vendor/phing/phing/classes/phing/types/selectors/AndSelector.php
  UY
  =&      G   vendor/phing/phing/classes/phing/types/selectors/BaseExtendSelector.php+	  UY+	  C!h      A   vendor/phing/phing/classes/phing/types/selectors/BaseSelector.php^  UY^  ="      J   vendor/phing/phing/classes/phing/types/selectors/BaseSelectorContainer.php   UY   	      K   vendor/phing/phing/classes/phing/types/selectors/ContainsRegexpSelector.php  UY  8      E   vendor/phing/phing/classes/phing/types/selectors/ContainsSelector.phpL  UYL  yQ      A   vendor/phing/phing/classes/phing/types/selectors/DateSelector.php  UY  Og      C   vendor/phing/phing/classes/phing/types/selectors/DependSelector.phpf  UYf        B   vendor/phing/phing/classes/phing/types/selectors/DepthSelector.php  UY  (      F   vendor/phing/phing/classes/phing/types/selectors/DifferentSelector.phpU  UYU  )c      G   vendor/phing/phing/classes/phing/types/selectors/ExtendFileSelector.php  UY  zҶ      C   vendor/phing/phing/classes/phing/types/selectors/ExtendSelector.php  UY  u      E   vendor/phing/phing/classes/phing/types/selectors/FilenameSelector.php  UY        A   vendor/phing/phing/classes/phing/types/selectors/FileSelector.php}  UY}  ~p`      E   vendor/phing/phing/classes/phing/types/selectors/MajoritySelector.phpc  UYc  z~D      D   vendor/phing/phing/classes/phing/types/selectors/MappingSelector.phpX  UYX  h      A   vendor/phing/phing/classes/phing/types/selectors/NoneSelector.php,
  UY,
  9P      @   vendor/phing/phing/classes/phing/types/selectors/NotSelector.phpX  UYX        ?   vendor/phing/phing/classes/phing/types/selectors/OrSelector.phpG
  UYG
  g7      D   vendor/phing/phing/classes/phing/types/selectors/PresentSelector.php  UY        E   vendor/phing/phing/classes/phing/types/selectors/ReadableSelector.php  UY  t      F   vendor/phing/phing/classes/phing/types/selectors/SelectorContainer.phpV  UYV  *X5      D   vendor/phing/phing/classes/phing/types/selectors/SelectorScanner.php  UY  g      B   vendor/phing/phing/classes/phing/types/selectors/SelectorUtils.php!  UY!  %      C   vendor/phing/phing/classes/phing/types/selectors/SelectSelector.php  UY  1      A   vendor/phing/phing/classes/phing/types/selectors/SizeSelector.php$  UY$  /:ȶ      A   vendor/phing/phing/classes/phing/types/selectors/TypeSelector.phpS  UYS  ޢ      E   vendor/phing/phing/classes/phing/types/selectors/WritableSelector.php  UY  r      6   vendor/phing/phing/classes/phing/types/TokenReader.php  UY  "H      6   vendor/phing/phing/classes/phing/types/TokenSource.php2  UY2  Nd(      3   vendor/phing/phing/classes/phing/UnknownElement.phpu   UYu   bѶ      3   vendor/phing/phing/classes/phing/util/DataStore.php  UY        :   vendor/phing/phing/classes/phing/util/DirectoryScanner.phpji  UYji  5˶      <   vendor/phing/phing/classes/phing/util/ExtendedFileStream.phps  UYs  w纶      3   vendor/phing/phing/classes/phing/util/FileUtils.php89  UY89        3   vendor/phing/phing/classes/phing/util/LogWriter.phpq	  UYq	        7   vendor/phing/phing/classes/phing/util/PathTokenizer.php9  UY9  ՠ      <   vendor/phing/phing/classes/phing/util/PearPackageScanner.php!  UY!  
Y߶      ;   vendor/phing/phing/classes/phing/util/regexp/PregEngine.php  UY  P?@      7   vendor/phing/phing/classes/phing/util/regexp/Regexp.php  UY  {d      =   vendor/phing/phing/classes/phing/util/regexp/RegexpEngine.php  UY  m1      ;   vendor/phing/phing/classes/phing/util/SourceFileScanner.phpm  UYm  me      6   vendor/phing/phing/classes/phing/util/StringHelper.php5   UY5   Bs      6   vendor/phpdocumentor/reflection-common/src/Element.php1  UY1  iUҶ      3   vendor/phpdocumentor/reflection-common/src/File.php7  UY7  3"      4   vendor/phpdocumentor/reflection-common/src/Fqsen.php  UY  C      7   vendor/phpdocumentor/reflection-common/src/Location.phpH  UYH  ?-      6   vendor/phpdocumentor/reflection-common/src/Project.php  UY  /H       =   vendor/phpdocumentor/reflection-common/src/ProjectFactory.php  UY  Q"ܶ      W   vendor/phpdocumentor/reflection-docblock/examples/01-interpreting-a-simple-docblock.php}  UY}  
B      J   vendor/phpdocumentor/reflection-docblock/examples/02-interpreting-tags.phpu  UYu  *:      R   vendor/phpdocumentor/reflection-docblock/examples/03-reconstituting-a-docblock.php  UY  uJ      L   vendor/phpdocumentor/reflection-docblock/examples/04-adding-your-own-tag.phpP  UYP  4wR      [   vendor/phpdocumentor/reflection-docblock/examples/playing-with-descriptions/02-escaping.php  UY  TDȶ      E   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Description.php  UY        L   vendor/phpdocumentor/reflection-docblock/src/DocBlock/DescriptionFactory.phpq  UYq  :      G   vendor/phpdocumentor/reflection-docblock/src/DocBlock/ExampleFinder.php  UY  +      D   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Serializer.php7  UY7  @]D      L   vendor/phpdocumentor/reflection-docblock/src/DocBlock/StandardTagFactory.phpx-  UYx-  ,K      =   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tag.phpu  UYu  ⹰      D   vendor/phpdocumentor/reflection-docblock/src/DocBlock/TagFactory.php  UY  P;Ͷ      E   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Author.php	  UY	  tc      F   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/BaseTag.php  UY  X
c      E   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Covers.phpJ  UYJ  XL      I   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Deprecated.php
  UY
  HO      F   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Example.phpQ  UYQ        S   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/StaticMethod.php  UY  2i      O   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/Strategy.php  UY  R      ]   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter/PassthroughFormatter.php%  UY%  I`      H   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter.php  UY  Dy7      F   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Generic.phpX
  UYX
  D       C   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Link.phpN  UYN  V      E   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Method.php  UY  o      D   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Param.php{  UY{  vO      G   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Property.php  UY  T̶      K   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyRead.php  UY  ѝݶ      L   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyWrite.php  UY        F   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Return_.php  UY  R      B   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/See.phpd  UYd   O      D   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Since.php	  UY	  P      E   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Source.phpm  UYm  d%      E   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Throws.php  UY  Ȉض      C   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Uses.phpP  UYP  T,      C   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Var_.php  UY  L&      F   vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Version.php	  UY	  d8      9   vendor/phpdocumentor/reflection-docblock/src/DocBlock.php  UY  /呶      @   vendor/phpdocumentor/reflection-docblock/src/DocBlockFactory.php$  UY$  Jl2      I   vendor/phpdocumentor/reflection-docblock/src/DocBlockFactoryInterface.php!  UY!  }      I   vendor/phpdocumentor/type-resolver/examples/01-resolving-simple-types.phpR  UYR  d[~      D   vendor/phpdocumentor/type-resolver/examples/02-resolving-classes.php  UY  `      I   vendor/phpdocumentor/type-resolver/examples/03-resolving-all-elements.phpO  UYO  ~$Ѷ      a   vendor/phpdocumentor/type-resolver/examples/04-discovering-the-context-using-class-reflection.php  UY  aӶ      b   vendor/phpdocumentor/type-resolver/examples/05-discovering-the-context-using-method-reflection.php  UY  4	ɶ      ^   vendor/phpdocumentor/type-resolver/examples/06-discovering-the-context-using-file-contents.php  UY  }      6   vendor/phpdocumentor/type-resolver/examples/Classy.php   UY   w      8   vendor/phpdocumentor/type-resolver/src/FqsenResolver.php  UY  ]Y      /   vendor/phpdocumentor/type-resolver/src/Type.php  UY  [L      7   vendor/phpdocumentor/type-resolver/src/TypeResolver.php"  UY"  W.      7   vendor/phpdocumentor/type-resolver/src/Types/Array_.phpN  UYN  ]ɤv      8   vendor/phpdocumentor/type-resolver/src/Types/Boolean.php  UY  f      :   vendor/phpdocumentor/type-resolver/src/Types/Callable_.php  UY  4ɿ      9   vendor/phpdocumentor/type-resolver/src/Types/Compound.php  UY  kd4b      8   vendor/phpdocumentor/type-resolver/src/Types/Context.php`  UY`  `-      ?   vendor/phpdocumentor/type-resolver/src/Types/ContextFactory.php  UY  ;      7   vendor/phpdocumentor/type-resolver/src/Types/Float_.php  UY  w,      8   vendor/phpdocumentor/type-resolver/src/Types/Integer.php  UY  "s      6   vendor/phpdocumentor/type-resolver/src/Types/Mixed.php  UY  Z      6   vendor/phpdocumentor/type-resolver/src/Types/Null_.php  UY  @%      8   vendor/phpdocumentor/type-resolver/src/Types/Object_.phpk  UYk        9   vendor/phpdocumentor/type-resolver/src/Types/Resource.php  UY  /      7   vendor/phpdocumentor/type-resolver/src/Types/Scalar.php  UY  U      6   vendor/phpdocumentor/type-resolver/src/Types/Self_.php  UY  9'      8   vendor/phpdocumentor/type-resolver/src/Types/Static_.phpU  UYU  ޟ      8   vendor/phpdocumentor/type-resolver/src/Types/String_.php  UY        5   vendor/phpdocumentor/type-resolver/src/Types/This.php  UY  h²      6   vendor/phpdocumentor/type-resolver/src/Types/Void_.phpW  UYW  ֶ      /   vendor/phpspec/prophecy/fixtures/EmptyClass.php:   UY:   =2X      3   vendor/phpspec/prophecy/fixtures/EmptyInterface.phpB   UYB         /   vendor/phpspec/prophecy/fixtures/FinalClass.php@   UY@         6   vendor/phpspec/prophecy/fixtures/ModifierInterface.php   UY   4      *   vendor/phpspec/prophecy/fixtures/Named.phpX   UYX   q$a      6   vendor/phpspec/prophecy/fixtures/OptionalDepsClass.php  UY  oT      3   vendor/phpspec/prophecy/fixtures/SpecialMethods.phpp  UYp  ('      2   vendor/phpspec/prophecy/fixtures/WithArguments.php   UY   <Z      9   vendor/phpspec/prophecy/fixtures/WithCallableArgument.php   UY    K,      4   vendor/phpspec/prophecy/fixtures/WithFinalMethod.php{   UY{         D   vendor/phpspec/prophecy/fixtures/WithFinalVirtuallyPrivateMethod.php   UY   <`      @   vendor/phpspec/prophecy/fixtures/WithProtectedAbstractMethod.php   UY   Co<      3   vendor/phpspec/prophecy/fixtures/WithReferences.php   UY   R      8   vendor/phpspec/prophecy/fixtures/WithReturnTypehints.php,  UY,  Z      5   vendor/phpspec/prophecy/fixtures/WithStaticMethod.phpu   UYu   Ӷ      C   vendor/phpspec/prophecy/fixtures/WithTypehintedVariadicArgument.php   UY   ۏ       9   vendor/phpspec/prophecy/fixtures/WithVariadicArgument.phpv   UYv   z϶      ?   vendor/phpspec/prophecy/fixtures/WithVirtuallyPrivateMethod.php   UY   ͣQ      H   vendor/phpspec/prophecy/spec/Prophecy/Argument/ArgumentsWildcardSpec.php&  UY&  6b      K   vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/AnyValuesTokenSpec.php=  UY=  j[r      J   vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/AnyValueTokenSpec.php-  UY-  DU      R   vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/ApproximateValueTokenSpec.php  UY  J4      L   vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/ArrayCountTokenSpec.php  UY  0      L   vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/ArrayEntryTokenSpec.php  UY  _6{      Q   vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/ArrayEveryEntryTokenSpec.php  UY  xw      J   vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/CallbackTokenSpec.php  UY  kv'      L   vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/ExactValueTokenSpec.php  UY  mݶ      P   vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/IdenticalValueTokenSpec.php  UY  Ӟ`      L   vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/LogicalAndTokenSpec.php  UY  R]      L   vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/LogicalNotTokenSpec.php  UY  d0Y      M   vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/ObjectStateTokenSpec.phpc  UYc  ]UW      P   vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/StringContainsTokenSpec.phps  UYs  5t      F   vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/TypeTokenSpec.php  UY  7      6   vendor/phpspec/prophecy/spec/Prophecy/ArgumentSpec.phpj  UYj  Hζ      =   vendor/phpspec/prophecy/spec/Prophecy/Call/CallCenterSpec.php[  UY[  w΄      7   vendor/phpspec/prophecy/spec/Prophecy/Call/CallSpec.php  UY  * #      J   vendor/phpspec/prophecy/spec/Prophecy/Comparator/ClosureComparatorSpec.php  UY  "\HL      @   vendor/phpspec/prophecy/spec/Prophecy/Comparator/FactorySpec.php  UY  nv      K   vendor/phpspec/prophecy/spec/Prophecy/Comparator/ProphecyComparatorSpec.php  UY  *      X   vendor/phpspec/prophecy/spec/Prophecy/Doubler/ClassPatch/DisableConstructorPatchSpec.php  UY  *Ķ      S   vendor/phpspec/prophecy/spec/Prophecy/Doubler/ClassPatch/HhvmExceptionPatchSpec.php  UY  5ւo      M   vendor/phpspec/prophecy/spec/Prophecy/Doubler/ClassPatch/KeywordPatchSpec.phpR  UYR  P      O   vendor/phpspec/prophecy/spec/Prophecy/Doubler/ClassPatch/MagicCallPatchSpec.phpa  UYa  z      U   vendor/phpspec/prophecy/spec/Prophecy/Doubler/ClassPatch/ProphecySubjectPatchSpec.php  UY        `   vendor/phpspec/prophecy/spec/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatchSpec.php  UY  +r      Q   vendor/phpspec/prophecy/spec/Prophecy/Doubler/ClassPatch/SplFileInfoPatchSpec.php|
  UY|
  c%      Q   vendor/phpspec/prophecy/spec/Prophecy/Doubler/ClassPatch/TraversablePatchSpec.phpw  UYw  P      =   vendor/phpspec/prophecy/spec/Prophecy/Doubler/DoublerSpec.php)  UY)  
      R   vendor/phpspec/prophecy/spec/Prophecy/Doubler/Generator/ClassCodeGeneratorSpec.phpj5  UYj5  v+      L   vendor/phpspec/prophecy/spec/Prophecy/Doubler/Generator/ClassCreatorSpec.phpg  UYg  Ǣ      Q   vendor/phpspec/prophecy/spec/Prophecy/Doubler/Generator/Node/ArgumentNodeSpec.php  UY  Lʶ      N   vendor/phpspec/prophecy/spec/Prophecy/Doubler/Generator/Node/ClassNodeSpec.php?  UY?        O   vendor/phpspec/prophecy/spec/Prophecy/Doubler/Generator/Node/MethodNodeSpec.php  UY  #      @   vendor/phpspec/prophecy/spec/Prophecy/Doubler/LazyDoubleSpec.php	  UY	  yu#      C   vendor/phpspec/prophecy/spec/Prophecy/Doubler/NameGeneratorSpec.php  UY  q      T   vendor/phpspec/prophecy/spec/Prophecy/Exception/Call/UnexpectedCallExceptionSpec.php  UY  ,]      U   vendor/phpspec/prophecy/spec/Prophecy/Exception/Doubler/ClassCreatorExceptionSpec.php}  UY}  U      T   vendor/phpspec/prophecy/spec/Prophecy/Exception/Doubler/ClassMirrorExceptionSpec.php=  UY=  &S      V   vendor/phpspec/prophecy/spec/Prophecy/Exception/Doubler/ClassNotFoundExceptionSpec.phpS  UYS  Y      O   vendor/phpspec/prophecy/spec/Prophecy/Exception/Doubler/DoubleExceptionSpec.phpF  UYF  F!      Z   vendor/phpspec/prophecy/spec/Prophecy/Exception/Doubler/InterfaceNotFoundExceptionSpec.php(  UY(  %;      \   vendor/phpspec/prophecy/spec/Prophecy/Exception/Doubler/MethodNotExtendableExceptionSpec.phpX  UYX        W   vendor/phpspec/prophecy/spec/Prophecy/Exception/Doubler/MethodNotFoundExceptionSpec.php  UY  fܧ%      U   vendor/phpspec/prophecy/spec/Prophecy/Exception/Prediction/AggregateExceptionSpec.phpA  UYA  (<      S   vendor/phpspec/prophecy/spec/Prophecy/Exception/Prediction/NoCallsExceptionSpec.php  UY         `   vendor/phpspec/prophecy/spec/Prophecy/Exception/Prediction/UnexpectedCallsCountExceptionSpec.php/  UY/  ݈      [   vendor/phpspec/prophecy/spec/Prophecy/Exception/Prediction/UnexpectedCallsExceptionSpec.php  UY  @      X   vendor/phpspec/prophecy/spec/Prophecy/Exception/Prophecy/MethodProphecyExceptionSpec.php  UY  ]      X   vendor/phpspec/prophecy/spec/Prophecy/Exception/Prophecy/ObjectProphecyExceptionSpec.phpK  UYK  Ҵ       K   vendor/phpspec/prophecy/spec/Prophecy/Prediction/CallbackPredictionSpec.phpJ  UYJ  Q      G   vendor/phpspec/prophecy/spec/Prophecy/Prediction/CallPredictionSpec.php:  UY:  v      L   vendor/phpspec/prophecy/spec/Prophecy/Prediction/CallTimesPredictionSpec.php  UY  `      J   vendor/phpspec/prophecy/spec/Prophecy/Prediction/NoCallsPredictionSpec.php.  UY.  OW>      E   vendor/phpspec/prophecy/spec/Prophecy/Promise/CallbackPromiseSpec.php	  UY	  u      K   vendor/phpspec/prophecy/spec/Prophecy/Promise/ReturnArgumentPromiseSpec.php  UY  ޽      C   vendor/phpspec/prophecy/spec/Prophecy/Promise/ReturnPromiseSpec.php  UY  2t      B   vendor/phpspec/prophecy/spec/Prophecy/Promise/ThrowPromiseSpec.phpe  UYe  I3      E   vendor/phpspec/prophecy/spec/Prophecy/Prophecy/MethodProphecySpec.php+1  UY+1  dg      E   vendor/phpspec/prophecy/spec/Prophecy/Prophecy/ObjectProphecySpec.phpA&  UYA&  1D1      ?   vendor/phpspec/prophecy/spec/Prophecy/Prophecy/RevealerSpec.phpq  UYq  r      5   vendor/phpspec/prophecy/spec/Prophecy/ProphetSpec.phpV
  UYV
  N      =   vendor/phpspec/prophecy/spec/Prophecy/Util/StringUtilSpec.php
  UY
  莶      C   vendor/phpspec/prophecy/src/Prophecy/Argument/ArgumentsWildcard.php4	  UY4	  A;K2      F   vendor/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValuesToken.php  UY  bN/      E   vendor/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValueToken.php  UY  Fh      M   vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ApproximateValueToken.php  UY  #I      G   vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayCountToken.php  UY  4̶      G   vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayEntryToken.php  UY  J:      L   vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayEveryEntryToken.php  UY  pb      E   vendor/phpspec/prophecy/src/Prophecy/Argument/Token/CallbackToken.php,  UY,  cR̶      G   vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ExactValueToken.php  UY   3      K   vendor/phpspec/prophecy/src/Prophecy/Argument/Token/IdenticalValueToken.php  UY        G   vendor/phpspec/prophecy/src/Prophecy/Argument/Token/LogicalAndToken.php  UY   Nv      G   vendor/phpspec/prophecy/src/Prophecy/Argument/Token/LogicalNotToken.php  UY  r      H   vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ObjectStateToken.php9
  UY9
  E.      K   vendor/phpspec/prophecy/src/Prophecy/Argument/Token/StringContainsToken.php  UY  >      F   vendor/phpspec/prophecy/src/Prophecy/Argument/Token/TokenInterface.php  UY  ٰ      A   vendor/phpspec/prophecy/src/Prophecy/Argument/Token/TypeToken.php  UY  n\      1   vendor/phpspec/prophecy/src/Prophecy/Argument.php  UY  AT      2   vendor/phpspec/prophecy/src/Prophecy/Call/Call.php	  UY	  {:%      8   vendor/phpspec/prophecy/src/Prophecy/Call/CallCenter.php  UY  nJhJ      E   vendor/phpspec/prophecy/src/Prophecy/Comparator/ClosureComparator.phpK  UYK  )RQ      ;   vendor/phpspec/prophecy/src/Prophecy/Comparator/Factory.php  UY  ֈi      F   vendor/phpspec/prophecy/src/Prophecy/Comparator/ProphecyComparator.phps  UYs  hǶ      >   vendor/phpspec/prophecy/src/Prophecy/Doubler/CachedDoubler.php  UY  ̇g      O   vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ClassPatchInterface.phpl  UYl  )5:      S   vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php  UY  :0`      N   vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php  UY  x^      H   vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/KeywordPatch.php  UY  /@ȶ      J   vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/MagicCallPatch.phpm	  UYm	  3.Ŷ      P   vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php  UY  k2H      [   vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.phpp  UYp  x      L   vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.phpQ
  UYQ
  [      L   vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/TraversablePatch.php	  UY	  jN      @   vendor/phpspec/prophecy/src/Prophecy/Doubler/DoubleInterface.php  UY  8dj      8   vendor/phpspec/prophecy/src/Prophecy/Doubler/Doubler.php  UY  8]^      M   vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCodeGenerator.php  UY  hLa^      G   vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCreator.php  UY  ?Br      F   vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassMirror.php(  UY(  )      L   vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ArgumentNode.php  UY  |      I   vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ClassNode.phpI  UYI  )Uݶ      J   vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/MethodNode.php;  UY;  +      N   vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ReflectionInterface.php  UY        ;   vendor/phpspec/prophecy/src/Prophecy/Doubler/LazyDouble.phpF  UYF  l      >   vendor/phpspec/prophecy/src/Prophecy/Doubler/NameGenerator.php  UY  7      O   vendor/phpspec/prophecy/src/Prophecy/Exception/Call/UnexpectedCallException.php  UY        P   vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassCreatorException.php  UY  77/%      O   vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassMirrorException.php  UY  ۉ?      Q   vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassNotFoundException.php  UY  h+      J   vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/DoubleException.php  UY  zF      K   vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/DoublerException.php  UY  Z^      U   vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/InterfaceNotFoundException.php  UY        W   vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/MethodNotExtendableException.phpD  UYD  p      R   vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/MethodNotFoundException.php  UY  ih      U   vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ReturnByReferenceException.php  UY        <   vendor/phpspec/prophecy/src/Prophecy/Exception/Exception.php+  UY+        K   vendor/phpspec/prophecy/src/Prophecy/Exception/InvalidArgumentException.php  UY  g      P   vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/AggregateException.php  UY  ?D<ζ      W   vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/FailedPredictionException.phpJ  UYJ  ~D      N   vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/NoCallsException.php  UY  l<      Q   vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/PredictionException.php  UY  2TѶ      [   vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php  UY  	ƶ      V   vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/UnexpectedCallsException.php,  UY,  a      S   vendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/MethodProphecyException.php)  UY)  F4      S   vendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/ObjectProphecyException.php  UY  :F      M   vendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/ProphecyException.php  UY  $϶      T   vendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.phpx  UYx  rЬ      H   vendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/ClassTagRetriever.phpD  UYD  d9϶      N   vendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/LegacyClassTagRetriever.phpo  UYo  u9      R   vendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php  UY  ;      F   vendor/phpspec/prophecy/src/Prophecy/Prediction/CallbackPrediction.php  UY  Vb{ζ      B   vendor/phpspec/prophecy/src/Prophecy/Prediction/CallPrediction.phpQ	  UYQ	  I      G   vendor/phpspec/prophecy/src/Prophecy/Prediction/CallTimesPrediction.php  UY  X      E   vendor/phpspec/prophecy/src/Prophecy/Prediction/NoCallsPrediction.php  UY  L9%      G   vendor/phpspec/prophecy/src/Prophecy/Prediction/PredictionInterface.php  UY  `IE      @   vendor/phpspec/prophecy/src/Prophecy/Promise/CallbackPromise.php  UY  [      A   vendor/phpspec/prophecy/src/Prophecy/Promise/PromiseInterface.phpK  UYK        F   vendor/phpspec/prophecy/src/Prophecy/Promise/ReturnArgumentPromise.php'  UY'  (      >   vendor/phpspec/prophecy/src/Prophecy/Promise/ReturnPromise.php  UY  ؏      =   vendor/phpspec/prophecy/src/Prophecy/Promise/ThrowPromise.php]  UY]  	~      @   vendor/phpspec/prophecy/src/Prophecy/Prophecy/MethodProphecy.phpZ/  UYZ/  l_r      @   vendor/phpspec/prophecy/src/Prophecy/Prophecy/ObjectProphecy.php  UY  5D      C   vendor/phpspec/prophecy/src/Prophecy/Prophecy/ProphecyInterface.php,  UY,  W      J   vendor/phpspec/prophecy/src/Prophecy/Prophecy/ProphecySubjectInterface.php  UY  i      :   vendor/phpspec/prophecy/src/Prophecy/Prophecy/Revealer.php  UY  jɸ      C   vendor/phpspec/prophecy/src/Prophecy/Prophecy/RevealerInterface.phpH  UYH  gZ      0   vendor/phpspec/prophecy/src/Prophecy/Prophet.php  UY  vq      8   vendor/phpspec/prophecy/src/Prophecy/Util/ExportUtil.phpP  UYP  2qƶ      8   vendor/phpspec/prophecy/src/Prophecy/Util/StringUtil.php	  UY	  %      5   vendor/phpunit/php-code-coverage/src/CodeCoverage.phpXt  UYXt  [f      6   vendor/phpunit/php-code-coverage/src/Driver/Driver.php  UY         4   vendor/phpunit/php-code-coverage/src/Driver/HHVM.php  UY  Ն7      6   vendor/phpunit/php-code-coverage/src/Driver/PHPDBG.php]  UY]  ӔӶ      6   vendor/phpunit/php-code-coverage/src/Driver/Xdebug.php
  UY
        R   vendor/phpunit/php-code-coverage/src/Exception/CoveredCodeNotExecutedException.php  UY  //      <   vendor/phpunit/php-code-coverage/src/Exception/Exception.php~  UY~  u      K   vendor/phpunit/php-code-coverage/src/Exception/InvalidArgumentException.php  UY  N      S   vendor/phpunit/php-code-coverage/src/Exception/MissingCoversAnnotationException.php  UY  fM      C   vendor/phpunit/php-code-coverage/src/Exception/RuntimeException.phpp  UYp  wC      V   vendor/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php  UY  F+      /   vendor/phpunit/php-code-coverage/src/Filter.php  UY  ʶ      :   vendor/phpunit/php-code-coverage/src/Node/AbstractNode.php  UY  _      5   vendor/phpunit/php-code-coverage/src/Node/Builder.phpb  UYb  *̶      7   vendor/phpunit/php-code-coverage/src/Node/Directory.php&  UY&  k      2   vendor/phpunit/php-code-coverage/src/Node/File.phpuL  UYuL  :      6   vendor/phpunit/php-code-coverage/src/Node/Iterator.php  UY  9Q      6   vendor/phpunit/php-code-coverage/src/Report/Clover.php&  UY&  $촶      6   vendor/phpunit/php-code-coverage/src/Report/Crap4j.php  UY  qro      ;   vendor/phpunit/php-code-coverage/src/Report/Html/Facade.php-  UY-  {$H      G   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.phpm&  UYm&  !mu      G   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Directory.php  UY  ;$      B   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/File.phpJ  UYJ  ̵      =   vendor/phpunit/php-code-coverage/src/Report/Html/Renderer.php"  UY"  d]      3   vendor/phpunit/php-code-coverage/src/Report/PHP.php  UY  DP      4   vendor/phpunit/php-code-coverage/src/Report/Text.php"  UY"  jڶ      <   vendor/phpunit/php-code-coverage/src/Report/Xml/Coverage.phpC  UYC  dȶ      =   vendor/phpunit/php-code-coverage/src/Report/Xml/Directory.phpR  UYR  &a      :   vendor/phpunit/php-code-coverage/src/Report/Xml/Facade.php  UY  P,      8   vendor/phpunit/php-code-coverage/src/Report/Xml/File.php  UY  e5j      :   vendor/phpunit/php-code-coverage/src/Report/Xml/Method.phpW  UYW  	\      8   vendor/phpunit/php-code-coverage/src/Report/Xml/Node.php  UY  ;#P      ;   vendor/phpunit/php-code-coverage/src/Report/Xml/Project.php  UY  ăJ      :   vendor/phpunit/php-code-coverage/src/Report/Xml/Report.php  UY  @wS      9   vendor/phpunit/php-code-coverage/src/Report/Xml/Tests.php  UY  M      :   vendor/phpunit/php-code-coverage/src/Report/Xml/Totals.php  UY  酮      8   vendor/phpunit/php-code-coverage/src/Report/Xml/Unit.php
  UY
  -5^      -   vendor/phpunit/php-code-coverage/src/Util.php  UY        /   vendor/phpunit/php-file-iterator/src/Facade.php  UY  0      0   vendor/phpunit/php-file-iterator/src/Factory.php	  UY	  y      1   vendor/phpunit/php-file-iterator/src/Iterator.phpa  UYa        1   vendor/phpunit/php-text-template/src/Template.php  UY  w4      &   vendor/phpunit/php-timer/src/Timer.php	  UY	  GT      C   vendor/phpunit/php-token-stream/src/Token/Stream/CachingFactory.php  UY  _      4   vendor/phpunit/php-token-stream/src/Token/Stream.phpA  UYA        -   vendor/phpunit/php-token-stream/src/Token.phpb  UYb  l<I      (   vendor/phpunit/phpunit/src/Exception.php9  UY9  ,ж      8   vendor/phpunit/phpunit/src/Extensions/GroupTestSuite.phpQ  UYQ  2ж      6   vendor/phpunit/phpunit/src/Extensions/PhptTestCase.php8-  UY8-  B      7   vendor/phpunit/phpunit/src/Extensions/PhptTestSuite.php  UY  M,n      6   vendor/phpunit/phpunit/src/Extensions/RepeatedTest.php  UY  O      7   vendor/phpunit/phpunit/src/Extensions/TestDecorator.php	  UY	  8<{      8   vendor/phpunit/phpunit/src/Extensions/TicketListener.php	  UY	  NeѶ      :   vendor/phpunit/phpunit/src/ForwardCompatibility/Assert.php]  UY]  _ȶ      H   vendor/phpunit/phpunit/src/ForwardCompatibility/AssertionFailedError.php~  UY~  2      D   vendor/phpunit/phpunit/src/ForwardCompatibility/BaseTestListener.php{  UY{  Z      8   vendor/phpunit/phpunit/src/ForwardCompatibility/Test.phpR  UYR  Qt      <   vendor/phpunit/phpunit/src/ForwardCompatibility/TestCase.phpc  UYc  ;¶      @   vendor/phpunit/phpunit/src/ForwardCompatibility/TestListener.phpj  UYj        =   vendor/phpunit/phpunit/src/ForwardCompatibility/TestSuite.php]  UY]  ؉ʶ      9   vendor/phpunit/phpunit/src/Framework/Assert/Functions.php  UY        /   vendor/phpunit/phpunit/src/Framework/Assert.php3^ UY3^ |ö      =   vendor/phpunit/phpunit/src/Framework/AssertionFailedError.phpJ  UYJ  ʋtG      9   vendor/phpunit/phpunit/src/Framework/BaseTestListener.php  UY  A%r      >   vendor/phpunit/phpunit/src/Framework/CodeCoverageException.php;  UY;  CzѶ      7   vendor/phpunit/phpunit/src/Framework/Constraint/And.php  UY  *}      ?   vendor/phpunit/phpunit/src/Framework/Constraint/ArrayHasKey.php  UY  *Q      ?   vendor/phpunit/phpunit/src/Framework/Constraint/ArraySubset.php
  UY
  o%Ҷ      =   vendor/phpunit/phpunit/src/Framework/Constraint/Attribute.phpd	  UYd	  ݻ      <   vendor/phpunit/phpunit/src/Framework/Constraint/Callback.php3  UY3        E   vendor/phpunit/phpunit/src/Framework/Constraint/ClassHasAttribute.php  UY  >      K   vendor/phpunit/phpunit/src/Framework/Constraint/ClassHasStaticAttribute.php  UY  U      =   vendor/phpunit/phpunit/src/Framework/Constraint/Composite.php  UY  R&      9   vendor/phpunit/phpunit/src/Framework/Constraint/Count.php  UY  lh      C   vendor/phpunit/phpunit/src/Framework/Constraint/DirectoryExists.php  UY  ڶ      =   vendor/phpunit/phpunit/src/Framework/Constraint/Exception.phpd  UYd        A   vendor/phpunit/phpunit/src/Framework/Constraint/ExceptionCode.php%  UY%  F7ڶ      D   vendor/phpunit/phpunit/src/Framework/Constraint/ExceptionMessage.php  UY  G      J   vendor/phpunit/phpunit/src/Framework/Constraint/ExceptionMessageRegExp.php*  UY*  ~2H      >   vendor/phpunit/phpunit/src/Framework/Constraint/FileExists.php  UY  z      ?   vendor/phpunit/phpunit/src/Framework/Constraint/GreaterThan.php  UY  y      >   vendor/phpunit/phpunit/src/Framework/Constraint/IsAnything.php  UY  B	      ;   vendor/phpunit/phpunit/src/Framework/Constraint/IsEmpty.php  UY  o      ;   vendor/phpunit/phpunit/src/Framework/Constraint/IsEqual.php  UY  ;߯      ;   vendor/phpunit/phpunit/src/Framework/Constraint/IsFalse.phpD  UYD  -      <   vendor/phpunit/phpunit/src/Framework/Constraint/IsFinite.phpH  UYH  d      ?   vendor/phpunit/phpunit/src/Framework/Constraint/IsIdentical.php  UY  qS.      >   vendor/phpunit/phpunit/src/Framework/Constraint/IsInfinite.phpP  UYP  os      @   vendor/phpunit/phpunit/src/Framework/Constraint/IsInstanceOf.php  UY  -Y      :   vendor/phpunit/phpunit/src/Framework/Constraint/IsJson.php  UY  KF      9   vendor/phpunit/phpunit/src/Framework/Constraint/IsNan.php<  UY<  ׾      :   vendor/phpunit/phpunit/src/Framework/Constraint/IsNull.php@  UY@  4      >   vendor/phpunit/phpunit/src/Framework/Constraint/IsReadable.php  UY  M      :   vendor/phpunit/phpunit/src/Framework/Constraint/IsTrue.php@  UY@  dXض      :   vendor/phpunit/phpunit/src/Framework/Constraint/IsType.php:  UY:  ζ      >   vendor/phpunit/phpunit/src/Framework/Constraint/IsWritable.php  UY  R      T   vendor/phpunit/phpunit/src/Framework/Constraint/JsonMatches/ErrorMessageProvider.php~  UY~  db      ?   vendor/phpunit/phpunit/src/Framework/Constraint/JsonMatches.php  UY  ٠b%      <   vendor/phpunit/phpunit/src/Framework/Constraint/LessThan.php  UY  6      7   vendor/phpunit/phpunit/src/Framework/Constraint/Not.php#  UY#  ,      F   vendor/phpunit/phpunit/src/Framework/Constraint/ObjectHasAttribute.phpl  UYl  ȶ      6   vendor/phpunit/phpunit/src/Framework/Constraint/Or.php
  UY
  ih      =   vendor/phpunit/phpunit/src/Framework/Constraint/PCREMatch.php  UY  ¶      <   vendor/phpunit/phpunit/src/Framework/Constraint/SameSize.php  UY  iͰ      B   vendor/phpunit/phpunit/src/Framework/Constraint/StringContains.php  UY  Vܶ      B   vendor/phpunit/phpunit/src/Framework/Constraint/StringEndsWith.php  UY  Ǧ      A   vendor/phpunit/phpunit/src/Framework/Constraint/StringMatches.php  UY  hz      D   vendor/phpunit/phpunit/src/Framework/Constraint/StringStartsWith.php  UY  :N      G   vendor/phpunit/phpunit/src/Framework/Constraint/TraversableContains.phpl  UYl  ':      K   vendor/phpunit/phpunit/src/Framework/Constraint/TraversableContainsOnly.php|	  UY|	  XĪ      7   vendor/phpunit/phpunit/src/Framework/Constraint/Xor.phpc  UYc  /-      3   vendor/phpunit/phpunit/src/Framework/Constraint.php  UY  E"9      H   vendor/phpunit/phpunit/src/Framework/CoveredCodeNotExecutedException.php  UY  Fuw      9   vendor/phpunit/phpunit/src/Framework/Error/Deprecated.php  UY  ,/      5   vendor/phpunit/phpunit/src/Framework/Error/Notice.php  UY  +I      6   vendor/phpunit/phpunit/src/Framework/Error/Warning.php  UY  bc:      .   vendor/phpunit/phpunit/src/Framework/Error.php  UY  x)      2   vendor/phpunit/phpunit/src/Framework/Exception.php  UY  Lɶ      9   vendor/phpunit/phpunit/src/Framework/ExceptionWrapper.phpg  UYg        C   vendor/phpunit/phpunit/src/Framework/ExpectationFailedException.phph  UYh  =ܞ      7   vendor/phpunit/phpunit/src/Framework/IncompleteTest.php  UY  y      ;   vendor/phpunit/phpunit/src/Framework/IncompleteTestCase.php  UY  =      <   vendor/phpunit/phpunit/src/Framework/IncompleteTestError.php  UY  T      E   vendor/phpunit/phpunit/src/Framework/InvalidCoversTargetException.phpN  UYN  iG      I   vendor/phpunit/phpunit/src/Framework/MissingCoversAnnotationException.php  UY  },      4   vendor/phpunit/phpunit/src/Framework/OutputError.php  UY  RB      2   vendor/phpunit/phpunit/src/Framework/RiskyTest.phpt  UYt  j~H      7   vendor/phpunit/phpunit/src/Framework/RiskyTestError.php  UY        7   vendor/phpunit/phpunit/src/Framework/SelfDescribing.php  UY  zl      4   vendor/phpunit/phpunit/src/Framework/SkippedTest.phpY  UYY  !l      8   vendor/phpunit/phpunit/src/Framework/SkippedTestCase.php  UY   Fն      9   vendor/phpunit/phpunit/src/Framework/SkippedTestError.php  UY  ࿶      >   vendor/phpunit/phpunit/src/Framework/SkippedTestSuiteError.php  UY  ٶ      7   vendor/phpunit/phpunit/src/Framework/SyntheticError.php  UY  7E      -   vendor/phpunit/phpunit/src/Framework/Test.phpZ  UYZ  @kD      1   vendor/phpunit/phpunit/src/Framework/TestCase.php UY Cbx      4   vendor/phpunit/phpunit/src/Framework/TestFailure.php  UY  yj"      5   vendor/phpunit/phpunit/src/Framework/TestListener.php  UY  ~"      3   vendor/phpunit/phpunit/src/Framework/TestResult.php  UY  \      ?   vendor/phpunit/phpunit/src/Framework/TestSuite/DataProvider.phpM  UYM  !_0      2   vendor/phpunit/phpunit/src/Framework/TestSuite.phpm  UYm  9?WĶ      H   vendor/phpunit/phpunit/src/Framework/UnintentionallyCoveredCodeError.php  UY  	Q      0   vendor/phpunit/phpunit/src/Framework/Warning.php?  UY?  {      8   vendor/phpunit/phpunit/src/Framework/WarningTestCase.php:  UY:  /      4   vendor/phpunit/phpunit/src/Runner/BaseTestRunner.phpJ  UYJ  (ܶ      /   vendor/phpunit/phpunit/src/Runner/Exception.php>  UY>  +Pݶ      4   vendor/phpunit/phpunit/src/Runner/Filter/Factory.php  UY  ^I       :   vendor/phpunit/phpunit/src/Runner/Filter/Group/Exclude.php  UY  T      :   vendor/phpunit/phpunit/src/Runner/Filter/Group/Include.php  UY  }f      2   vendor/phpunit/phpunit/src/Runner/Filter/Group.php  UY  7_w      1   vendor/phpunit/phpunit/src/Runner/Filter/Test.phpq  UYq  |O      =   vendor/phpunit/phpunit/src/Runner/StandardTestSuiteLoader.php  UY        5   vendor/phpunit/phpunit/src/Runner/TestSuiteLoader.php  UY  \fb      -   vendor/phpunit/phpunit/src/Runner/Version.php$  UY$  :#9      -   vendor/phpunit/phpunit/src/TextUI/Command.php  UY  |.      3   vendor/phpunit/phpunit/src/TextUI/ResultPrinter.phpoF  UYoF  K      0   vendor/phpunit/phpunit/src/TextUI/TestRunner.phpξ  UYξ  R      -   vendor/phpunit/phpunit/src/Util/Blacklist.phpr  UYr  a1      1   vendor/phpunit/phpunit/src/Util/Configuration.phpI  UYI  Z7"      :   vendor/phpunit/phpunit/src/Util/ConfigurationGenerator.phpF  UYF  Q      0   vendor/phpunit/phpunit/src/Util/ErrorHandler.phpL  UYL  ӱs      .   vendor/phpunit/phpunit/src/Util/Fileloader.phpv  UYv  (E      .   vendor/phpunit/phpunit/src/Util/Filesystem.php   UY   Ӷ      *   vendor/phpunit/phpunit/src/Util/Filter.php[  UY[  ފ,      *   vendor/phpunit/phpunit/src/Util/Getopt.php  UY  1      /   vendor/phpunit/phpunit/src/Util/GlobalState.php:  UY:  9"      9   vendor/phpunit/phpunit/src/Util/InvalidArgumentHelper.php"  UY"        ,   vendor/phpunit/phpunit/src/Util/Log/JSON.php  UY  g      -   vendor/phpunit/phpunit/src/Util/Log/JUnit.php2  UY2  )      +   vendor/phpunit/phpunit/src/Util/Log/TAP.phpr  UYr  n߶      0   vendor/phpunit/phpunit/src/Util/Log/TeamCity.phpD+  UYD+  \5W      /   vendor/phpunit/phpunit/src/Util/PHP/Default.php  UY  n      2   vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php  UY   3      /   vendor/phpunit/phpunit/src/Util/PHP/Windows.php  UY  Ŀ      '   vendor/phpunit/phpunit/src/Util/PHP.php)  UY)  OP      +   vendor/phpunit/phpunit/src/Util/Printer.phpJ  UYJ  Lz      )   vendor/phpunit/phpunit/src/Util/Regex.phpq  UYq  V      *   vendor/phpunit/phpunit/src/Util/String.phpm  UYm        (   vendor/phpunit/phpunit/src/Util/Test.phpZ  UYZ  "B      :   vendor/phpunit/phpunit/src/Util/TestDox/NamePrettifier.phpC  UYC  k      >   vendor/phpunit/phpunit/src/Util/TestDox/ResultPrinter/HTML.php
  UY
  
      >   vendor/phpunit/phpunit/src/Util/TestDox/ResultPrinter/Text.phpF  UYF   !kT      =   vendor/phpunit/phpunit/src/Util/TestDox/ResultPrinter/XML.php  UY  [      9   vendor/phpunit/phpunit/src/Util/TestDox/ResultPrinter.phpf%  UYf%  >'      5   vendor/phpunit/phpunit/src/Util/TestSuiteIterator.php  UY        (   vendor/phpunit/phpunit/src/Util/Type.phpH  UYH  VV߶      '   vendor/phpunit/phpunit/src/Util/XML.php  UY  f      Q   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Identity.php  UY  w[      Y   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/InvocationMocker.php  UY  ϶      N   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Match.php  UY  jZ      X   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/MethodNameMatch.php  UY  $      R   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Namespace.php  UY  AF      X   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/ParametersMatch.php  UY  w2      M   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Stub.phpX  UYX  ѕ      a   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/BadMethodCallException.php  UY  ).      T   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/Exception.php  UY  ]T      [   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/RuntimeException.php  UY  Yn4      J   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Generator.phpY  UYY  ۶      R   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation/Object.php  UY  8      R   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation/Static.php  UY  R      K   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation.php  UY  9T      Q   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/InvocationMocker.php  UY  `8      J   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invokable.php  UY         X   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/AnyInvokedCount.php  UY  Ds$      V   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/AnyParameters.phpI  UYI  U@      ^   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/ConsecutiveParameters.php  UY  o"ī      S   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/Invocation.php  UY  @⸩      W   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtIndex.php	  UY	  7y{      \   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtLeastCount.php  UY  08      [   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtLeastOnce.php  UY  E@G      [   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtMostCount.php  UY  ̈́-      U   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedCount.php  UY  q      X   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedRecorder.phpb  UYb  i      S   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/MethodName.php-  UY-  =R      S   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/Parameters.php  UY  	}(      \   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/StatelessInvocation.phpi  UYi        H   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher.phpi!  UYi!  7ZRж      L   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/MockBuilder.phpI#  UYI#  y      K   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/MockObject.php  UY        V   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ConsecutiveCalls.php  UY  2	      O   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/Exception.php&  UY&  _,
      W   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/MatcherCollection.php  UY  v      L   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/Return.php  UY  Lf+      T   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnArgument.php  UY  9)`      T   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnCallback.php  UY  g`      U   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnReference.php;  UY;  l      P   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnSelf.php  UY  fkk      T   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnValueMap.php|  UY|  uB	      E   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub.php  UY        K   vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Verifiable.php  UY  'L      8   vendor/sebastian/code-unit-reverse-lookup/src/Wizard.phpe  UYe        3   vendor/sebastian/comparator/src/ArrayComparator.php`  UY`        .   vendor/sebastian/comparator/src/Comparator.php  UY  `      5   vendor/sebastian/comparator/src/ComparisonFailure.php  UY  V      6   vendor/sebastian/comparator/src/DateTimeComparator.phpP
  UYP
  RO^      5   vendor/sebastian/comparator/src/DOMNodeComparator.phpS  UYS  Wb      4   vendor/sebastian/comparator/src/DoubleComparator.phpp  UYp  vz      7   vendor/sebastian/comparator/src/ExceptionComparator.php  UY  kf       +   vendor/sebastian/comparator/src/Factory.phpd  UYd  ه1      8   vendor/sebastian/comparator/src/MockObjectComparator.php  UY  O      5   vendor/sebastian/comparator/src/NumericComparator.php  UY  _nѶ      4   vendor/sebastian/comparator/src/ObjectComparator.php  UY  26      6   vendor/sebastian/comparator/src/ResourceComparator.php*  UY*  ٫Ķ      4   vendor/sebastian/comparator/src/ScalarComparator.phpw  UYw  ׶      >   vendor/sebastian/comparator/src/SplObjectStorageComparator.php  UY  W      2   vendor/sebastian/comparator/src/TypeComparator.php  UY  G϶      #   vendor/sebastian/diff/src/Chunk.php  UY  v|      "   vendor/sebastian/diff/src/Diff.php  UY  Sdh      $   vendor/sebastian/diff/src/Differ.php)  UY)  S9R      :   vendor/sebastian/diff/src/LCS/LongestCommonSubsequence.phpj  UYj  \/      W   vendor/sebastian/diff/src/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.phpR	  UYR	  o      U   vendor/sebastian/diff/src/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php  UY        "   vendor/sebastian/diff/src/Line.php  UY  eܶ      $   vendor/sebastian/diff/src/Parser.php  UY  &Ƕ      ,   vendor/sebastian/environment/src/Console.php  UY  F      ,   vendor/sebastian/environment/src/Runtime.phpz  UYz  O<      *   vendor/sebastian/exporter/src/Exporter.phpE#  UYE#        /   vendor/sebastian/global-state/src/Blacklist.php[  UY[  :      2   vendor/sebastian/global-state/src/CodeExporter.php  UY  `(C      /   vendor/sebastian/global-state/src/Exception.php?  UY?        .   vendor/sebastian/global-state/src/Restorer.php  UY  ӓ;\      6   vendor/sebastian/global-state/src/RuntimeException.phpq  UYq  ~]!      .   vendor/sebastian/global-state/src/Snapshot.php%  UY%  ÖSݶ      5   vendor/sebastian/object-enumerator/src/Enumerator.phpk	  UYk	  Q	$      4   vendor/sebastian/object-enumerator/src/Exception.php6  UY6  n$*a      C   vendor/sebastian/object-enumerator/src/InvalidArgumentException.phpx  UYx  '      2   vendor/sebastian/recursion-context/src/Context.php{  UY{        4   vendor/sebastian/recursion-context/src/Exception.phpJ  UYJ        C   vendor/sebastian/recursion-context/src/InvalidArgumentException.php  UY  mH      7   vendor/sebastian/resource-operations/build/generate.php  UY  x4      ?   vendor/sebastian/resource-operations/src/ResourceOperations.phpU  UYU  hն      (   vendor/sebastian/version/src/Version.php  UY  N\Ƕ      A   vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php  UY  j,ն      B   vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.phpV-  UYV-  ^9HR      K   vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php#  UY#  Ӗc      9   vendor/symfony/event-dispatcher/Debug/WrappedListener.php  UY  M}      M   vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.phpX  UYX  6m      )   vendor/symfony/event-dispatcher/Event.php  UY  <Bz      3   vendor/symfony/event-dispatcher/EventDispatcher.php  UY  07ݶ      <   vendor/symfony/event-dispatcher/EventDispatcherInterface.php  UY  	      <   vendor/symfony/event-dispatcher/EventSubscriberInterface.php  UY        0   vendor/symfony/event-dispatcher/GenericEvent.php  UY  7R      <   vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php~	  UY~	  (      +   vendor/symfony/yaml/Command/LintCommand.php  UY  A>         vendor/symfony/yaml/Dumper.phpx  UYx           vendor/symfony/yaml/Escaper.phpg  UYg  bڔ      /   vendor/symfony/yaml/Exception/DumpException.php  UY        4   vendor/symfony/yaml/Exception/ExceptionInterface.php  UY  ^KA      0   vendor/symfony/yaml/Exception/ParseException.phpv  UYv  ~Yʶ      2   vendor/symfony/yaml/Exception/RuntimeException.php  UY  _q         vendor/symfony/yaml/Inline.phpz  UYz  }!         vendor/symfony/yaml/Parser.php  UY  (ض      '   vendor/symfony/yaml/Tag/TaggedValue.phpG  UYG  iY      !   vendor/symfony/yaml/Unescaper.phpQ  UYQ  !+׶         vendor/symfony/yaml/Yaml.php.  UY.  hH      &   vendor/webmozart/assert/src/Assert.php~  UY~  O,         src/OpenTok/Archive.php  UY  Vڶ         src/OpenTok/ArchiveList.php  UY  %4N         src/OpenTok/ArchiveMode.phpX  UYX  RfQ         src/OpenTok/Broadcast.phpL  UYL  J      8   src/OpenTok/Exception/ArchiveAuthenticationException.phpF  UYF        0   src/OpenTok/Exception/ArchiveDomainException.php  UY  E7      *   src/OpenTok/Exception/ArchiveException.php   UY   u      9   src/OpenTok/Exception/ArchiveUnexpectedValueException.phpV  UYV  "ţ-      1   src/OpenTok/Exception/AuthenticationException.phpi  UYi  !b      :   src/OpenTok/Exception/BroadcastAuthenticationException.phpI  UYI  UHͶ      2   src/OpenTok/Exception/BroadcastDomainException.php  UY  [      ,   src/OpenTok/Exception/BroadcastException.php   UY   ն      ;   src/OpenTok/Exception/BroadcastUnexpectedValueException.phpY  UYY  M      )   src/OpenTok/Exception/DomainException.php  UY  윶      #   src/OpenTok/Exception/Exception.php   UY         2   src/OpenTok/Exception/InvalidArgumentException.php  UY  m'      2   src/OpenTok/Exception/UnexpectedValueException.php  UY  ^pA϶         src/OpenTok/Layout.php  UY            src/OpenTok/MediaMode.phpa  UYa  ܶ         src/OpenTok/OpenTok.phpf\  UYf\  p         src/OpenTok/OutputMode.php  UY  uζ         src/OpenTok/Role.php  UY  J[         src/OpenTok/Session.phpZ  UYZ  ł         src/OpenTok/SipCall.php  UY  J*      $   src/OpenTok/Util/archive-schema.json#  UY#  &ڬ         src/OpenTok/Util/BasicEnum.phpO  UYO  [Tg      &   src/OpenTok/Util/broadcast-schema.json3  UY3  \         src/OpenTok/Util/Client.php0  UY0  @kJ      '   src/OpenTok/Util/Plugin/OpentokAuth.php  UY  >m         src/OpenTok/Util/Validators.php.  UY.  $J         tools/stub.php   UY   CͶ      <?php

// autoload.php @generated by Composer

require_once __DIR__ . '/composer/autoload_real.php';

return ComposerAutoloaderInit25d6eaeff17a07513aba0f926ea4d0e9::getLoader();
<?php

// autoload_classmap.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'AbstractFileSet' => $vendorDir . '/phing/phing/classes/phing/types/AbstractFileSet.php',
    'AbstractHandler' => $vendorDir . '/phing/phing/classes/phing/parser/AbstractHandler.php',
    'AbstractLiquibaseTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/liquibase/AbstractLiquibaseTask.php',
    'AbstractPHPLocFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phploc/AbstractPHPLocFormatter.php',
    'AbstractPropertySetterTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/property/AbstractPropertySetterTask.php',
    'AbstractSAXParser' => $vendorDir . '/phing/phing/classes/phing/parser/AbstractSAXParser.php',
    'AbstractSelectorContainer' => $vendorDir . '/phing/phing/classes/phing/types/selectors/AbstractSelectorContainer.php',
    'AdhocTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/AdhocTask.php',
    'AdhocTaskdefTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/AdhocTaskdefTask.php',
    'AdhocTypedefTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/AdhocTypedefTask.php',
    'AndCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/AndCondition.php',
    'AndSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/AndSelector.php',
    'AnsiColorLogger' => $vendorDir . '/phing/phing/classes/phing/listener/AnsiColorLogger.php',
    'ApiGenTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/apigen/ApiGenTask.php',
    'AppendTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/AppendTask.php',
    'ApplyTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/ApplyTask.php',
    'Arg' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/SymfonyConsole/Arg.php',
    'AssignedVar' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/CapsuleTask.php',
    'AttribTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/AttribTask.php',
    'AutoloaderTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/AutoloaderTask.php',
    'AvailableTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/AvailableTask.php',
    'BaseExtendSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/BaseExtendSelector.php',
    'BaseFilterReader' => $vendorDir . '/phing/phing/classes/phing/filters/BaseFilterReader.php',
    'BaseParamFilterReader' => $vendorDir . '/phing/phing/classes/phing/filters/BaseParamFilterReader.php',
    'BaseSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/BaseSelector.php',
    'BaseSelectorContainer' => $vendorDir . '/phing/phing/classes/phing/types/selectors/BaseSelectorContainer.php',
    'Basename' => $vendorDir . '/phing/phing/classes/phing/tasks/system/Basename.php',
    'BatchTest' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpunit/BatchTest.php',
    'BlockForTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/BlockForTask.php',
    'BufferedReader' => $vendorDir . '/phing/phing/classes/phing/system/io/BufferedReader.php',
    'BufferedWriter' => $vendorDir . '/phing/phing/classes/phing/system/io/BufferedWriter.php',
    'BuildEvent' => $vendorDir . '/phing/phing/classes/phing/BuildEvent.php',
    'BuildException' => $vendorDir . '/phing/phing/classes/phing/BuildException.php',
    'BuildListener' => $vendorDir . '/phing/phing/classes/phing/BuildListener.php',
    'BuildLogger' => $vendorDir . '/phing/phing/classes/phing/BuildLogger.php',
    'BuildTimeoutException' => $vendorDir . '/phing/phing/classes/phing/BuildTimeoutException.php',
    'Capsule' => $vendorDir . '/phing/phing/classes/phing/lib/Capsule.php',
    'CapsuleTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/CapsuleTask.php',
    'CaseTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/SwitchTask.php',
    'ChainReaderHelper' => $vendorDir . '/phing/phing/classes/phing/filters/util/ChainReaderHelper.php',
    'ChainableReader' => $vendorDir . '/phing/phing/classes/phing/filters/ChainableReader.php',
    'ChainedMapper' => $vendorDir . '/phing/phing/classes/phing/mappers/ChainedMapper.php',
    'Character' => $vendorDir . '/phing/phing/classes/phing/system/lang/Character.php',
    'ChmodTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/ChmodTask.php',
    'ChownTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/ChownTask.php',
    'CloverPHPUnitResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpunit/formatter/CloverPHPUnitResultFormatter.php',
    'Commandline' => $vendorDir . '/phing/phing/classes/phing/types/Commandline.php',
    'CommandlineArgument' => $vendorDir . '/phing/phing/classes/phing/types/Commandline.php',
    'CommandlineMarker' => $vendorDir . '/phing/phing/classes/phing/types/Commandline.php',
    'Comment' => $vendorDir . '/phing/phing/classes/phing/filters/StripLineComments.php',
    'ComposerTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/ComposerTask.php',
    'CompositeMapper' => $vendorDir . '/phing/phing/classes/phing/mappers/CompositeMapper.php',
    'ConcatFilter' => $vendorDir . '/phing/phing/classes/phing/filters/ConcatFilter.php',
    'Condition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/Condition.php',
    'ConditionBase' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/ConditionBase.php',
    'ConditionEnumeration' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/ConditionBase.php',
    'ConditionTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/ConditionTask.php',
    'ConfigurationException' => $vendorDir . '/phing/phing/classes/phing/ConfigurationException.php',
    'ConsoleReader' => $vendorDir . '/phing/phing/classes/phing/system/io/ConsoleReader.php',
    'ContainerMapper' => $vendorDir . '/phing/phing/classes/phing/mappers/ContainerMapper.php',
    'Contains' => $vendorDir . '/phing/phing/classes/phing/filters/LineContains.php',
    'ContainsCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/ContainsCondition.php',
    'ContainsRegexpSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/ContainsRegexpSelector.php',
    'ContainsSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/ContainsSelector.php',
    'CopyTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/CopyTask.php',
    'CoverageMerger' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/coverage/CoverageMerger.php',
    'CoverageMergerTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/coverage/CoverageMergerTask.php',
    'CoverageReportTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/coverage/CoverageReportTask.php',
    'CoverageReportTransformer' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/coverage/CoverageReportTransformer.php',
    'CoverageSetupTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/coverage/CoverageSetupTask.php',
    'CoverageThresholdTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/coverage/CoverageThresholdTask.php',
    'Crap4jPHPUnitResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpunit/formatter/Crap4jPHPUnitResultFormatter.php',
    'CreoleSQLExecTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/creole/CreoleSQLExecTask.php',
    'CreoleTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/creole/CreoleTask.php',
    'CustomChildCreator' => $vendorDir . '/phing/phing/classes/phing/parser/CustomChildCreator.php',
    'CutDirsMapper' => $vendorDir . '/phing/phing/classes/phing/mappers/CutDirsMapper.php',
    'CvsPassTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/CvsPassTask.php',
    'CvsTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/CvsTask.php',
    'DataStore' => $vendorDir . '/phing/phing/classes/phing/util/DataStore.php',
    'DataType' => $vendorDir . '/phing/phing/classes/phing/types/DataType.php',
    'DataTypeHandler' => $vendorDir . '/phing/phing/classes/phing/parser/DataTypeHandler.php',
    'DateSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/DateSelector.php',
    'DbDeployTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/dbdeploy/DbDeployTask.php',
    'DbmsSyntax' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntax.php',
    'DbmsSyntaxFactory' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxFactory.php',
    'DbmsSyntaxMsSql' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxMsSql.php',
    'DbmsSyntaxMysql' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxMysql.php',
    'DbmsSyntaxOracle' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxOracle.php',
    'DbmsSyntaxPgSQL' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxPgSQL.php',
    'DbmsSyntaxSQLite' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxSQLite.php',
    'DefaultInputHandler' => $vendorDir . '/phing/phing/classes/phing/input/DefaultInputHandler.php',
    'DefaultLogger' => $vendorDir . '/phing/phing/classes/phing/listener/DefaultLogger.php',
    'DefaultPDOQuerySplitter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/pdo/DefaultPDOQuerySplitter.php',
    'DefaultPHPCPDResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpcpd/formatter/DefaultPHPCPDResultFormatter.php',
    'DeleteTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/DeleteTask.php',
    'DependSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/DependSelector.php',
    'DepthSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/DepthSelector.php',
    'Description' => $vendorDir . '/phing/phing/classes/phing/types/Description.php',
    'Diagnostics' => $vendorDir . '/phing/phing/classes/phing/Diagnostics.php',
    'DiagnosticsTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/DiagnosticsTask.php',
    'DifferentSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/DifferentSelector.php',
    'DirSet' => $vendorDir . '/phing/phing/classes/phing/types/DirSet.php',
    'DirectoryScanner' => $vendorDir . '/phing/phing/classes/phing/util/DirectoryScanner.php',
    'Dirname' => $vendorDir . '/phing/phing/classes/phing/tasks/system/Dirname.php',
    'DocBlox_Parallel_Manager' => $vendorDir . '/phing/phing/classes/phing/contrib/DocBlox/Parallel/Manager.php',
    'DocBlox_Parallel_Worker' => $vendorDir . '/phing/phing/classes/phing/contrib/DocBlox/Parallel/Worker.php',
    'DocBlox_Parallel_WorkerPipe' => $vendorDir . '/phing/phing/classes/phing/contrib/DocBlox/Parallel/WorkerPipe.php',
    'DummyPDOQuerySplitter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/pdo/DummyPDOQuerySplitter.php',
    'EchoProperties' => $vendorDir . '/phing/phing/classes/phing/tasks/system/EchoProperties.php',
    'EchoTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/EchoTask.php',
    'ElementHandler' => $vendorDir . '/phing/phing/classes/phing/parser/ElementHandler.php',
    'ElseIfTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/IfTask.php',
    'EqualsCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/EqualsCondition.php',
    'EscapeUnicode' => $vendorDir . '/phing/phing/classes/phing/filters/EscapeUnicode.php',
    'EventObject' => $vendorDir . '/phing/phing/classes/phing/system/lang/EventObject.php',
    'Excludes' => $vendorDir . '/phing/phing/classes/phing/types/Excludes.php',
    'ExcludesNameEntry' => $vendorDir . '/phing/phing/classes/phing/types/ExcludesNameEntry.php',
    'ExecTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/ExecTask.php',
    'ExitStatusException' => $vendorDir . '/phing/phing/classes/phing/ExitStatusException.php',
    'ExpandProperties' => $vendorDir . '/phing/phing/classes/phing/filters/ExpandProperties.php',
    'ExpatParseException' => $vendorDir . '/phing/phing/classes/phing/parser/ExpatParseException.php',
    'ExpatParser' => $vendorDir . '/phing/phing/classes/phing/parser/ExpatParser.php',
    'ExportPropertiesTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/ExportPropertiesTask.php',
    'ExtendFileSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/ExtendFileSelector.php',
    'ExtendSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/ExtendSelector.php',
    'ExtendedFileStream' => $vendorDir . '/phing/phing/classes/phing/util/ExtendedFileStream.php',
    'ExtractBaseTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/ExtractBaseTask.php',
    'FailTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/FailTask.php',
    'FileHashTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/FileHashTask.php',
    'FileInputStream' => $vendorDir . '/phing/phing/classes/phing/system/io/FileInputStream.php',
    'FileList' => $vendorDir . '/phing/phing/classes/phing/types/FileList.php',
    'FileNameMapper' => $vendorDir . '/phing/phing/classes/phing/mappers/FileNameMapper.php',
    'FileNotFoundException' => $vendorDir . '/phing/phing/classes/phing/system/lang/FileNotFoundException.php',
    'FileOutputStream' => $vendorDir . '/phing/phing/classes/phing/system/io/FileOutputStream.php',
    'FileParserFactory' => $vendorDir . '/phing/phing/classes/phing/system/io/FileParserFactory.php',
    'FileParserFactoryInterface' => $vendorDir . '/phing/phing/classes/phing/system/io/FileParserFactoryInterface.php',
    'FileParserInterface' => $vendorDir . '/phing/phing/classes/phing/system/io/FileParserInterface.php',
    'FileReader' => $vendorDir . '/phing/phing/classes/phing/system/io/FileReader.php',
    'FileSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/FileSelector.php',
    'FileSet' => $vendorDir . '/phing/phing/classes/phing/types/FileSet.php',
    'FileSizeTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/FileSizeTask.php',
    'FileSyncTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/FileSyncTask.php',
    'FileSystem' => $vendorDir . '/phing/phing/classes/phing/system/io/FileSystem.php',
    'FileUtils' => $vendorDir . '/phing/phing/classes/phing/util/FileUtils.php',
    'FileWriter' => $vendorDir . '/phing/phing/classes/phing/system/io/FileWriter.php',
    'File_Iterator' => $vendorDir . '/phpunit/php-file-iterator/src/Iterator.php',
    'File_Iterator_Facade' => $vendorDir . '/phpunit/php-file-iterator/src/Facade.php',
    'File_Iterator_Factory' => $vendorDir . '/phpunit/php-file-iterator/src/Factory.php',
    'FilenameSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/FilenameSelector.php',
    'FilesMatch' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/FilesMatch.php',
    'FilterChain' => $vendorDir . '/phing/phing/classes/phing/types/FilterChain.php',
    'FilterReader' => $vendorDir . '/phing/phing/classes/phing/system/io/FilterReader.php',
    'FirstMatchMapper' => $vendorDir . '/phing/phing/classes/phing/mappers/FirstMatchMapper.php',
    'FlattenMapper' => $vendorDir . '/phing/phing/classes/phing/mappers/FlattenMapper.php',
    'ForeachTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/ForeachTask.php',
    'FormatterElement' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpunit/FormatterElement.php',
    'FtpDeployTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/FtpDeployTask.php',
    'FunctionParam' => $vendorDir . '/phing/phing/classes/phing/tasks/system/PhpEvalTask.php',
    'GitBaseTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/git/GitBaseTask.php',
    'GitBranchTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/git/GitBranchTask.php',
    'GitCheckoutTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/git/GitCheckoutTask.php',
    'GitCloneTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/git/GitCloneTask.php',
    'GitCommitTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/git/GitCommitTask.php',
    'GitDescribeTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/git/GitDescribeTask.php',
    'GitFetchTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/git/GitFetchTask.php',
    'GitGcTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/git/GitGcTask.php',
    'GitInitTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/git/GitInitTask.php',
    'GitLogTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/git/GitLogTask.php',
    'GitMergeTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/git/GitMergeTask.php',
    'GitPullTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/git/GitPullTask.php',
    'GitPushTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/git/GitPushTask.php',
    'GitTagTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/git/GitTagTask.php',
    'GlobMapper' => $vendorDir . '/phing/phing/classes/phing/mappers/GlobMapper.php',
    'GrowlNotifyTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/GrowlNotifyTask.php',
    'HasFreeSpaceCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/HasFreeSpaceCondition.php',
    'HeadFilter' => $vendorDir . '/phing/phing/classes/phing/filters/HeadFilter.php',
    'HgAddTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/hg/HgAddTask.php',
    'HgArchiveTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/hg/HgArchiveTask.php',
    'HgBaseTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/hg/HgBaseTask.php',
    'HgCloneTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/hg/HgCloneTask.php',
    'HgCommitTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/hg/HgCommitTask.php',
    'HgInitTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/hg/HgInitTask.php',
    'HgLogTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/hg/HgLogTask.php',
    'HgPullTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/hg/HgPullTask.php',
    'HgPushTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/hg/HgPushTask.php',
    'HgRevertTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/hg/HgRevertTask.php',
    'HgTagTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/hg/HgTagTask.php',
    'HgUpdateTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/hg/HgUpdateTask.php',
    'HtmlColorLogger' => $vendorDir . '/phing/phing/classes/phing/listener/HtmlColorLogger.php',
    'HttpCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/HttpCondition.php',
    'HttpGetTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/HttpGetTask.php',
    'HttpRequestTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/HttpRequestTask.php',
    'HttpTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/HttpTask.php',
    'IOException' => $vendorDir . '/phing/phing/classes/phing/system/io/IOException.php',
    'IconvFilter' => $vendorDir . '/phing/phing/classes/phing/filters/IconvFilter.php',
    'IdentityMapper' => $vendorDir . '/phing/phing/classes/phing/mappers/IdentityMapper.php',
    'IfTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/IfTask.php',
    'ImportTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/ImportTask.php',
    'IncludePathTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/IncludePathTask.php',
    'IniFileConfig' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/inifile/IniFileConfig.php',
    'IniFileParser' => $vendorDir . '/phing/phing/classes/phing/system/io/IniFileParser.php',
    'IniFileRemove' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/inifile/IniFileRemove.php',
    'IniFileSet' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/inifile/IniFileSet.php',
    'IniFileTokenReader' => $vendorDir . '/phing/phing/classes/phing/filters/util/IniFileTokenReader.php',
    'InifileTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/inifile/IniFileTask.php',
    'InputHandler' => $vendorDir . '/phing/phing/classes/phing/input/InputHandler.php',
    'InputRequest' => $vendorDir . '/phing/phing/classes/phing/input/InputRequest.php',
    'InputStream' => $vendorDir . '/phing/phing/classes/phing/system/io/InputStream.php',
    'InputStreamReader' => $vendorDir . '/phing/phing/classes/phing/system/io/InputStreamReader.php',
    'InputTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/InputTask.php',
    'IntrospectionHelper' => $vendorDir . '/phing/phing/classes/phing/IntrospectionHelper.php',
    'IoncubeComment' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/ioncube/IoncubeComment.php',
    'IoncubeEncoderTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/ioncube/IoncubeEncoderTask.php',
    'IoncubeLicenseTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/ioncube/IoncubeLicenseTask.php',
    'IsFailure' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/IsFailure.php',
    'IsFalseCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/IsFalseCondition.php',
    'IsFileSelected' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/IsFileSelected.php',
    'IsPropertyFalseCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/IsPropertyFalseCondition.php',
    'IsPropertyTrueCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/IsPropertyTrueCondition.php',
    'IsSetCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/IsSetCondition.php',
    'IsTrueCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/IsTrueCondition.php',
    'IterableFileSet' => $vendorDir . '/phing/phing/classes/phing/types/IterableFileSet.php',
    'JsHintTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/JsHintTask.php',
    'JsMinTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/jsmin/JsMinTask.php',
    'JslLintTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/JslLintTask.php',
    'JsonLogger' => $vendorDir . '/phing/phing/classes/phing/listener/JsonLogger.php',
    'LineContains' => $vendorDir . '/phing/phing/classes/phing/filters/LineContains.php',
    'LineContainsRegexp' => $vendorDir . '/phing/phing/classes/phing/filters/LineContainsRegexp.php',
    'LiquibaseChangeLogTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseChangeLogTask.php',
    'LiquibaseDbDocTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseDbDocTask.php',
    'LiquibaseDiffTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseDiffTask.php',
    'LiquibaseParameter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/liquibase/AbstractLiquibaseTask.php',
    'LiquibaseProperty' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/liquibase/AbstractLiquibaseTask.php',
    'LiquibaseRollbackTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseRollbackTask.php',
    'LiquibaseTagTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseTagTask.php',
    'LiquibaseTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseTask.php',
    'LiquibaseUpdateTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseUpdateTask.php',
    'LoadFileTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/LoadFileTask.php',
    'Location' => $vendorDir . '/phing/phing/classes/phing/parser/Location.php',
    'LogWriter' => $vendorDir . '/phing/phing/classes/phing/util/LogWriter.php',
    'MailLogger' => $vendorDir . '/phing/phing/classes/phing/listener/MailLogger.php',
    'MailTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/MailTask.php',
    'MajoritySelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/MajoritySelector.php',
    'ManifestTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/ManifestTask.php',
    'MapEntry' => $vendorDir . '/phing/phing/classes/phing/tasks/system/PathConvert.php',
    'Mapper' => $vendorDir . '/phing/phing/classes/phing/types/Mapper.php',
    'MappingSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/MappingSelector.php',
    'Matches' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/Matches.php',
    'MatchingTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/MatchingTask.php',
    'MergeMapper' => $vendorDir . '/phing/phing/classes/phing/mappers/MergeMapper.php',
    'MkdirTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/MkdirTask.php',
    'MoveTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/MoveTask.php',
    'MultipleChoiceInputRequest' => $vendorDir . '/phing/phing/classes/phing/input/MultipleChoiceInputRequest.php',
    'NestedCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/NestedCondition.php',
    'NoBannerLogger' => $vendorDir . '/phing/phing/classes/phing/listener/NoBannerLogger.php',
    'NoneSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/NoneSelector.php',
    'NotCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/NotCondition.php',
    'NotSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/NotSelector.php',
    'NotifySendTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/NotifySendTask.php',
    'NullPointerException' => $vendorDir . '/phing/phing/classes/phing/system/lang/NullPointerException.php',
    'OrCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/OrCondition.php',
    'OrSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/OrSelector.php',
    'OsCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/OsCondition.php',
    'OutputStream' => $vendorDir . '/phing/phing/classes/phing/system/io/OutputStream.php',
    'OutputStreamWriter' => $vendorDir . '/phing/phing/classes/phing/system/io/OutputStreamWriter.php',
    'PDOQuerySplitter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/pdo/PDOQuerySplitter.php',
    'PDOResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/pdo/PDOResultFormatter.php',
    'PDOSQLExecFormatterElement' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/pdo/PDOSQLExecFormatterElement.php',
    'PDOSQLExecTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/pdo/PDOSQLExecTask.php',
    'PDOSQLExecTransaction' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/pdo/PDOSQLExecTask.php',
    'PDOTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/pdo/PDOTask.php',
    'PEAR_PackageFileManager_Fileset' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/pearpackage/Fileset.php',
    'PHPCPDFormatterElement' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpcpd/PHPCPDFormatterElement.php',
    'PHPCPDResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpcpd/formatter/PHPCPDResultFormatter.php',
    'PHPCPDTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpcpd/PHPCPDTask.php',
    'PHPLocCSVFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phploc/PHPLocCSVFormatter.php',
    'PHPLocFormatterElement' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phploc/PHPLocFormatterElement.php',
    'PHPLocFormatterFactory' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phploc/PHPLocFormatterFactory.php',
    'PHPLocTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phploc/PHPLocTask.php',
    'PHPLocTextFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phploc/PHPLocTextFormatter.php',
    'PHPLocXMLFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phploc/PHPLocXMLFormatter.php',
    'PHPMDFormatterElement' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpmd/PHPMDFormatterElement.php',
    'PHPMDRendererRemoveFromCache' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpmd/PHPMDRendererRemoveFromCache.php',
    'PHPMDTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpmd/PHPMDTask.php',
    'PHPUnitReportTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpunit/PHPUnitReportTask.php',
    'PHPUnitResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpunit/formatter/PHPUnitResultFormatter.php',
    'PHPUnitTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpunit/PHPUnitTask.php',
    'PHPUnitTestRunner' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpunit/PHPUnitTestRunner.php',
    'PHPUnitUtil' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpunit/PHPUnitUtil.php',
    'PHPUnit\\Framework\\Assert' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/Assert.php',
    'PHPUnit\\Framework\\AssertionFailedError' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/AssertionFailedError.php',
    'PHPUnit\\Framework\\BaseTestListener' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/BaseTestListener.php',
    'PHPUnit\\Framework\\Test' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/Test.php',
    'PHPUnit\\Framework\\TestCase' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/TestCase.php',
    'PHPUnit\\Framework\\TestListener' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/TestListener.php',
    'PHPUnit\\Framework\\TestSuite' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/TestSuite.php',
    'PHPUnit_Exception' => $vendorDir . '/phpunit/phpunit/src/Exception.php',
    'PHPUnit_Extensions_GroupTestSuite' => $vendorDir . '/phpunit/phpunit/src/Extensions/GroupTestSuite.php',
    'PHPUnit_Extensions_PhptTestCase' => $vendorDir . '/phpunit/phpunit/src/Extensions/PhptTestCase.php',
    'PHPUnit_Extensions_PhptTestSuite' => $vendorDir . '/phpunit/phpunit/src/Extensions/PhptTestSuite.php',
    'PHPUnit_Extensions_RepeatedTest' => $vendorDir . '/phpunit/phpunit/src/Extensions/RepeatedTest.php',
    'PHPUnit_Extensions_TestDecorator' => $vendorDir . '/phpunit/phpunit/src/Extensions/TestDecorator.php',
    'PHPUnit_Extensions_TicketListener' => $vendorDir . '/phpunit/phpunit/src/Extensions/TicketListener.php',
    'PHPUnit_Framework_Assert' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert.php',
    'PHPUnit_Framework_AssertionFailedError' => $vendorDir . '/phpunit/phpunit/src/Framework/AssertionFailedError.php',
    'PHPUnit_Framework_BaseTestListener' => $vendorDir . '/phpunit/phpunit/src/Framework/BaseTestListener.php',
    'PHPUnit_Framework_CodeCoverageException' => $vendorDir . '/phpunit/phpunit/src/Framework/CodeCoverageException.php',
    'PHPUnit_Framework_Constraint' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint.php',
    'PHPUnit_Framework_Constraint_And' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/And.php',
    'PHPUnit_Framework_Constraint_ArrayHasKey' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ArrayHasKey.php',
    'PHPUnit_Framework_Constraint_ArraySubset' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ArraySubset.php',
    'PHPUnit_Framework_Constraint_Attribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Attribute.php',
    'PHPUnit_Framework_Constraint_Callback' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Callback.php',
    'PHPUnit_Framework_Constraint_ClassHasAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ClassHasAttribute.php',
    'PHPUnit_Framework_Constraint_ClassHasStaticAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ClassHasStaticAttribute.php',
    'PHPUnit_Framework_Constraint_Composite' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Composite.php',
    'PHPUnit_Framework_Constraint_Count' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Count.php',
    'PHPUnit_Framework_Constraint_DirectoryExists' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/DirectoryExists.php',
    'PHPUnit_Framework_Constraint_Exception' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Exception.php',
    'PHPUnit_Framework_Constraint_ExceptionCode' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ExceptionCode.php',
    'PHPUnit_Framework_Constraint_ExceptionMessage' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessage.php',
    'PHPUnit_Framework_Constraint_ExceptionMessageRegExp' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessageRegExp.php',
    'PHPUnit_Framework_Constraint_FileExists' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/FileExists.php',
    'PHPUnit_Framework_Constraint_GreaterThan' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/GreaterThan.php',
    'PHPUnit_Framework_Constraint_IsAnything' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsAnything.php',
    'PHPUnit_Framework_Constraint_IsEmpty' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsEmpty.php',
    'PHPUnit_Framework_Constraint_IsEqual' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsEqual.php',
    'PHPUnit_Framework_Constraint_IsFalse' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsFalse.php',
    'PHPUnit_Framework_Constraint_IsFinite' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsFinite.php',
    'PHPUnit_Framework_Constraint_IsIdentical' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsIdentical.php',
    'PHPUnit_Framework_Constraint_IsInfinite' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsInfinite.php',
    'PHPUnit_Framework_Constraint_IsInstanceOf' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsInstanceOf.php',
    'PHPUnit_Framework_Constraint_IsJson' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsJson.php',
    'PHPUnit_Framework_Constraint_IsNan' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsNan.php',
    'PHPUnit_Framework_Constraint_IsNull' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsNull.php',
    'PHPUnit_Framework_Constraint_IsReadable' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsReadable.php',
    'PHPUnit_Framework_Constraint_IsTrue' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsTrue.php',
    'PHPUnit_Framework_Constraint_IsType' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsType.php',
    'PHPUnit_Framework_Constraint_IsWritable' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsWritable.php',
    'PHPUnit_Framework_Constraint_JsonMatches' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/JsonMatches.php',
    'PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/JsonMatches/ErrorMessageProvider.php',
    'PHPUnit_Framework_Constraint_LessThan' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/LessThan.php',
    'PHPUnit_Framework_Constraint_Not' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Not.php',
    'PHPUnit_Framework_Constraint_ObjectHasAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ObjectHasAttribute.php',
    'PHPUnit_Framework_Constraint_Or' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Or.php',
    'PHPUnit_Framework_Constraint_PCREMatch' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/PCREMatch.php',
    'PHPUnit_Framework_Constraint_SameSize' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/SameSize.php',
    'PHPUnit_Framework_Constraint_StringContains' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/StringContains.php',
    'PHPUnit_Framework_Constraint_StringEndsWith' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/StringEndsWith.php',
    'PHPUnit_Framework_Constraint_StringMatches' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/StringMatches.php',
    'PHPUnit_Framework_Constraint_StringStartsWith' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/StringStartsWith.php',
    'PHPUnit_Framework_Constraint_TraversableContains' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/TraversableContains.php',
    'PHPUnit_Framework_Constraint_TraversableContainsOnly' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/TraversableContainsOnly.php',
    'PHPUnit_Framework_Constraint_Xor' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Xor.php',
    'PHPUnit_Framework_CoveredCodeNotExecutedException' => $vendorDir . '/phpunit/phpunit/src/Framework/CoveredCodeNotExecutedException.php',
    'PHPUnit_Framework_Error' => $vendorDir . '/phpunit/phpunit/src/Framework/Error.php',
    'PHPUnit_Framework_Error_Deprecated' => $vendorDir . '/phpunit/phpunit/src/Framework/Error/Deprecated.php',
    'PHPUnit_Framework_Error_Notice' => $vendorDir . '/phpunit/phpunit/src/Framework/Error/Notice.php',
    'PHPUnit_Framework_Error_Warning' => $vendorDir . '/phpunit/phpunit/src/Framework/Error/Warning.php',
    'PHPUnit_Framework_Exception' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception.php',
    'PHPUnit_Framework_ExceptionWrapper' => $vendorDir . '/phpunit/phpunit/src/Framework/ExceptionWrapper.php',
    'PHPUnit_Framework_ExpectationFailedException' => $vendorDir . '/phpunit/phpunit/src/Framework/ExpectationFailedException.php',
    'PHPUnit_Framework_IncompleteTest' => $vendorDir . '/phpunit/phpunit/src/Framework/IncompleteTest.php',
    'PHPUnit_Framework_IncompleteTestCase' => $vendorDir . '/phpunit/phpunit/src/Framework/IncompleteTestCase.php',
    'PHPUnit_Framework_IncompleteTestError' => $vendorDir . '/phpunit/phpunit/src/Framework/IncompleteTestError.php',
    'PHPUnit_Framework_InvalidCoversTargetException' => $vendorDir . '/phpunit/phpunit/src/Framework/InvalidCoversTargetException.php',
    'PHPUnit_Framework_MissingCoversAnnotationException' => $vendorDir . '/phpunit/phpunit/src/Framework/MissingCoversAnnotationException.php',
    'PHPUnit_Framework_MockObject_BadMethodCallException' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/BadMethodCallException.php',
    'PHPUnit_Framework_MockObject_Builder_Identity' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Identity.php',
    'PHPUnit_Framework_MockObject_Builder_InvocationMocker' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/InvocationMocker.php',
    'PHPUnit_Framework_MockObject_Builder_Match' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Match.php',
    'PHPUnit_Framework_MockObject_Builder_MethodNameMatch' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/MethodNameMatch.php',
    'PHPUnit_Framework_MockObject_Builder_Namespace' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Namespace.php',
    'PHPUnit_Framework_MockObject_Builder_ParametersMatch' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/ParametersMatch.php',
    'PHPUnit_Framework_MockObject_Builder_Stub' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Stub.php',
    'PHPUnit_Framework_MockObject_Exception' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/Exception.php',
    'PHPUnit_Framework_MockObject_Generator' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Generator.php',
    'PHPUnit_Framework_MockObject_Invocation' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation.php',
    'PHPUnit_Framework_MockObject_InvocationMocker' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/InvocationMocker.php',
    'PHPUnit_Framework_MockObject_Invocation_Object' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation/Object.php',
    'PHPUnit_Framework_MockObject_Invocation_Static' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation/Static.php',
    'PHPUnit_Framework_MockObject_Invokable' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invokable.php',
    'PHPUnit_Framework_MockObject_Matcher' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher.php',
    'PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/AnyInvokedCount.php',
    'PHPUnit_Framework_MockObject_Matcher_AnyParameters' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/AnyParameters.php',
    'PHPUnit_Framework_MockObject_Matcher_ConsecutiveParameters' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/ConsecutiveParameters.php',
    'PHPUnit_Framework_MockObject_Matcher_Invocation' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/Invocation.php',
    'PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtIndex.php',
    'PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastCount' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtLeastCount.php',
    'PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtLeastOnce.php',
    'PHPUnit_Framework_MockObject_Matcher_InvokedAtMostCount' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtMostCount.php',
    'PHPUnit_Framework_MockObject_Matcher_InvokedCount' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedCount.php',
    'PHPUnit_Framework_MockObject_Matcher_InvokedRecorder' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedRecorder.php',
    'PHPUnit_Framework_MockObject_Matcher_MethodName' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/MethodName.php',
    'PHPUnit_Framework_MockObject_Matcher_Parameters' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/Parameters.php',
    'PHPUnit_Framework_MockObject_Matcher_StatelessInvocation' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/StatelessInvocation.php',
    'PHPUnit_Framework_MockObject_MockBuilder' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/MockBuilder.php',
    'PHPUnit_Framework_MockObject_MockObject' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/MockObject.php',
    'PHPUnit_Framework_MockObject_RuntimeException' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/RuntimeException.php',
    'PHPUnit_Framework_MockObject_Stub' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub.php',
    'PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ConsecutiveCalls.php',
    'PHPUnit_Framework_MockObject_Stub_Exception' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/Exception.php',
    'PHPUnit_Framework_MockObject_Stub_MatcherCollection' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/MatcherCollection.php',
    'PHPUnit_Framework_MockObject_Stub_Return' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/Return.php',
    'PHPUnit_Framework_MockObject_Stub_ReturnArgument' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnArgument.php',
    'PHPUnit_Framework_MockObject_Stub_ReturnCallback' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnCallback.php',
    'PHPUnit_Framework_MockObject_Stub_ReturnReference' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnReference.php',
    'PHPUnit_Framework_MockObject_Stub_ReturnSelf' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnSelf.php',
    'PHPUnit_Framework_MockObject_Stub_ReturnValueMap' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnValueMap.php',
    'PHPUnit_Framework_MockObject_Verifiable' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Verifiable.php',
    'PHPUnit_Framework_OutputError' => $vendorDir . '/phpunit/phpunit/src/Framework/OutputError.php',
    'PHPUnit_Framework_RiskyTest' => $vendorDir . '/phpunit/phpunit/src/Framework/RiskyTest.php',
    'PHPUnit_Framework_RiskyTestError' => $vendorDir . '/phpunit/phpunit/src/Framework/RiskyTestError.php',
    'PHPUnit_Framework_SelfDescribing' => $vendorDir . '/phpunit/phpunit/src/Framework/SelfDescribing.php',
    'PHPUnit_Framework_SkippedTest' => $vendorDir . '/phpunit/phpunit/src/Framework/SkippedTest.php',
    'PHPUnit_Framework_SkippedTestCase' => $vendorDir . '/phpunit/phpunit/src/Framework/SkippedTestCase.php',
    'PHPUnit_Framework_SkippedTestError' => $vendorDir . '/phpunit/phpunit/src/Framework/SkippedTestError.php',
    'PHPUnit_Framework_SkippedTestSuiteError' => $vendorDir . '/phpunit/phpunit/src/Framework/SkippedTestSuiteError.php',
    'PHPUnit_Framework_SyntheticError' => $vendorDir . '/phpunit/phpunit/src/Framework/SyntheticError.php',
    'PHPUnit_Framework_Test' => $vendorDir . '/phpunit/phpunit/src/Framework/Test.php',
    'PHPUnit_Framework_TestCase' => $vendorDir . '/phpunit/phpunit/src/Framework/TestCase.php',
    'PHPUnit_Framework_TestFailure' => $vendorDir . '/phpunit/phpunit/src/Framework/TestFailure.php',
    'PHPUnit_Framework_TestListener' => $vendorDir . '/phpunit/phpunit/src/Framework/TestListener.php',
    'PHPUnit_Framework_TestResult' => $vendorDir . '/phpunit/phpunit/src/Framework/TestResult.php',
    'PHPUnit_Framework_TestSuite' => $vendorDir . '/phpunit/phpunit/src/Framework/TestSuite.php',
    'PHPUnit_Framework_TestSuite_DataProvider' => $vendorDir . '/phpunit/phpunit/src/Framework/TestSuite/DataProvider.php',
    'PHPUnit_Framework_UnintentionallyCoveredCodeError' => $vendorDir . '/phpunit/phpunit/src/Framework/UnintentionallyCoveredCodeError.php',
    'PHPUnit_Framework_Warning' => $vendorDir . '/phpunit/phpunit/src/Framework/Warning.php',
    'PHPUnit_Framework_WarningTestCase' => $vendorDir . '/phpunit/phpunit/src/Framework/WarningTestCase.php',
    'PHPUnit_Runner_BaseTestRunner' => $vendorDir . '/phpunit/phpunit/src/Runner/BaseTestRunner.php',
    'PHPUnit_Runner_Exception' => $vendorDir . '/phpunit/phpunit/src/Runner/Exception.php',
    'PHPUnit_Runner_Filter_Factory' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/Factory.php',
    'PHPUnit_Runner_Filter_GroupFilterIterator' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/Group.php',
    'PHPUnit_Runner_Filter_Group_Exclude' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/Group/Exclude.php',
    'PHPUnit_Runner_Filter_Group_Include' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/Group/Include.php',
    'PHPUnit_Runner_Filter_Test' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/Test.php',
    'PHPUnit_Runner_StandardTestSuiteLoader' => $vendorDir . '/phpunit/phpunit/src/Runner/StandardTestSuiteLoader.php',
    'PHPUnit_Runner_TestSuiteLoader' => $vendorDir . '/phpunit/phpunit/src/Runner/TestSuiteLoader.php',
    'PHPUnit_Runner_Version' => $vendorDir . '/phpunit/phpunit/src/Runner/Version.php',
    'PHPUnit_TextUI_Command' => $vendorDir . '/phpunit/phpunit/src/TextUI/Command.php',
    'PHPUnit_TextUI_ResultPrinter' => $vendorDir . '/phpunit/phpunit/src/TextUI/ResultPrinter.php',
    'PHPUnit_TextUI_TestRunner' => $vendorDir . '/phpunit/phpunit/src/TextUI/TestRunner.php',
    'PHPUnit_Util_Blacklist' => $vendorDir . '/phpunit/phpunit/src/Util/Blacklist.php',
    'PHPUnit_Util_Configuration' => $vendorDir . '/phpunit/phpunit/src/Util/Configuration.php',
    'PHPUnit_Util_ConfigurationGenerator' => $vendorDir . '/phpunit/phpunit/src/Util/ConfigurationGenerator.php',
    'PHPUnit_Util_ErrorHandler' => $vendorDir . '/phpunit/phpunit/src/Util/ErrorHandler.php',
    'PHPUnit_Util_Fileloader' => $vendorDir . '/phpunit/phpunit/src/Util/Fileloader.php',
    'PHPUnit_Util_Filesystem' => $vendorDir . '/phpunit/phpunit/src/Util/Filesystem.php',
    'PHPUnit_Util_Filter' => $vendorDir . '/phpunit/phpunit/src/Util/Filter.php',
    'PHPUnit_Util_Getopt' => $vendorDir . '/phpunit/phpunit/src/Util/Getopt.php',
    'PHPUnit_Util_GlobalState' => $vendorDir . '/phpunit/phpunit/src/Util/GlobalState.php',
    'PHPUnit_Util_InvalidArgumentHelper' => $vendorDir . '/phpunit/phpunit/src/Util/InvalidArgumentHelper.php',
    'PHPUnit_Util_Log_JSON' => $vendorDir . '/phpunit/phpunit/src/Util/Log/JSON.php',
    'PHPUnit_Util_Log_JUnit' => $vendorDir . '/phpunit/phpunit/src/Util/Log/JUnit.php',
    'PHPUnit_Util_Log_TAP' => $vendorDir . '/phpunit/phpunit/src/Util/Log/TAP.php',
    'PHPUnit_Util_Log_TeamCity' => $vendorDir . '/phpunit/phpunit/src/Util/Log/TeamCity.php',
    'PHPUnit_Util_PHP' => $vendorDir . '/phpunit/phpunit/src/Util/PHP.php',
    'PHPUnit_Util_PHP_Default' => $vendorDir . '/phpunit/phpunit/src/Util/PHP/Default.php',
    'PHPUnit_Util_PHP_Windows' => $vendorDir . '/phpunit/phpunit/src/Util/PHP/Windows.php',
    'PHPUnit_Util_Printer' => $vendorDir . '/phpunit/phpunit/src/Util/Printer.php',
    'PHPUnit_Util_Regex' => $vendorDir . '/phpunit/phpunit/src/Util/Regex.php',
    'PHPUnit_Util_String' => $vendorDir . '/phpunit/phpunit/src/Util/String.php',
    'PHPUnit_Util_Test' => $vendorDir . '/phpunit/phpunit/src/Util/Test.php',
    'PHPUnit_Util_TestDox_NamePrettifier' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/NamePrettifier.php',
    'PHPUnit_Util_TestDox_ResultPrinter' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter.php',
    'PHPUnit_Util_TestDox_ResultPrinter_HTML' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter/HTML.php',
    'PHPUnit_Util_TestDox_ResultPrinter_Text' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter/Text.php',
    'PHPUnit_Util_TestDox_ResultPrinter_XML' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter/XML.php',
    'PHPUnit_Util_TestSuiteIterator' => $vendorDir . '/phpunit/phpunit/src/Util/TestSuiteIterator.php',
    'PHPUnit_Util_Type' => $vendorDir . '/phpunit/phpunit/src/Util/Type.php',
    'PHPUnit_Util_XML' => $vendorDir . '/phpunit/phpunit/src/Util/XML.php',
    'PHP_CodeSniffer_Reports_PhingRemoveFromCache' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpcs/Reports_PhingRemoveFromCache.php',
    'PHP_Timer' => $vendorDir . '/phpunit/php-timer/src/Timer.php',
    'PHP_Token' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_TokenWithScope' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_TokenWithScopeAndVisibility' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ABSTRACT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_AMPERSAND' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_AND_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ARRAY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ARRAY_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_AS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ASYNC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_AT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_AWAIT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_BACKTICK' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_BAD_CHARACTER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_BOOLEAN_AND' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_BOOLEAN_OR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_BOOL_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_BREAK' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CALLABLE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CARET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CASE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CATCH' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CHARACTER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CLASS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CLASS_C' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CLASS_NAME_CONSTANT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CLONE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CLOSE_BRACKET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CLOSE_CURLY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CLOSE_SQUARE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CLOSE_TAG' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_COALESCE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_COLON' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_COMMA' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_COMMENT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_COMPILER_HALT_OFFSET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CONCAT_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CONST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CONSTANT_ENCAPSED_STRING' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CONTINUE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_CURLY_OPEN' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_DEC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_DECLARE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_DEFAULT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_DIR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_DIV' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_DIV_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_DNUMBER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_DO' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_DOC_COMMENT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_DOLLAR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_DOLLAR_OPEN_CURLY_BRACES' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_DOT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_DOUBLE_ARROW' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_DOUBLE_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_DOUBLE_COLON' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_DOUBLE_QUOTES' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ECHO' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ELLIPSIS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ELSE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ELSEIF' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_EMPTY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ENCAPSED_AND_WHITESPACE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ENDDECLARE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ENDFOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ENDFOREACH' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ENDIF' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ENDSWITCH' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ENDWHILE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_END_HEREDOC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ENUM' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_EQUALS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_EVAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_EXCLAMATION_MARK' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_EXIT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_EXTENDS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_FILE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_FINAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_FINALLY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_FOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_FOREACH' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_FUNCTION' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_FUNC_C' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_GLOBAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_GOTO' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_GT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_HALT_COMPILER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_IF' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_IMPLEMENTS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_IN' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_INC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_INCLUDE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_INCLUDE_ONCE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_INLINE_HTML' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_INSTANCEOF' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_INSTEADOF' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_INTERFACE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_INT_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ISSET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_IS_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_IS_GREATER_OR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_IS_IDENTICAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_IS_NOT_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_IS_NOT_IDENTICAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_IS_SMALLER_OR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_Includes' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_JOIN' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_LAMBDA_ARROW' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_LAMBDA_CP' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_LAMBDA_OP' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_LINE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_LIST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_LNUMBER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_LOGICAL_AND' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_LOGICAL_OR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_LOGICAL_XOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_LT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_METHOD_C' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_MINUS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_MINUS_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_MOD_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_MULT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_MUL_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_NAMESPACE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_NEW' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_NS_C' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_NS_SEPARATOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_NULLSAFE_OBJECT_OPERATOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_NUM_STRING' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_OBJECT_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_OBJECT_OPERATOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_ONUMBER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_OPEN_BRACKET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_OPEN_CURLY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_OPEN_SQUARE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_OPEN_TAG' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_OPEN_TAG_WITH_ECHO' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_OR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_PAAMAYIM_NEKUDOTAYIM' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_PERCENT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_PIPE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_PLUS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_PLUS_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_POW' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_POW_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_PRINT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_PRIVATE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_PROTECTED' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_PUBLIC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_QUESTION_MARK' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_REQUIRE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_REQUIRE_ONCE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_RETURN' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_SEMICOLON' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_SHAPE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_SL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_SL_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_SPACESHIP' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_SR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_SR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_START_HEREDOC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_STATIC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_STRING' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_STRING_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_STRING_VARNAME' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_SUPER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_SWITCH' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_Stream' => $vendorDir . '/phpunit/php-token-stream/src/Token/Stream.php',
    'PHP_Token_Stream_CachingFactory' => $vendorDir . '/phpunit/php-token-stream/src/Token/Stream/CachingFactory.php',
    'PHP_Token_THROW' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_TILDE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_TRAIT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_TRAIT_C' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_TRY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_TYPE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_TYPELIST_GT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_TYPELIST_LT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_UNSET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_UNSET_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_USE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_USE_FUNCTION' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_VAR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_VARIABLE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_WHERE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_WHILE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_WHITESPACE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_XHP_ATTRIBUTE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_XHP_CATEGORY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_XHP_CATEGORY_LABEL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_XHP_CHILDREN' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_XHP_LABEL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_XHP_REQUIRED' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_XHP_TAG_GT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_XHP_TAG_LT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_XHP_TEXT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_XOR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_YIELD' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PHP_Token_YIELD_FROM' => $vendorDir . '/phpunit/php-token-stream/src/Token.php',
    'PMDPHPCPDResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpcpd/formatter/PMDPHPCPDResultFormatter.php',
    'PackageAsPathTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/PackageAsPathTask.php',
    'ParallelTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/ParallelTask.php',
    'Parameter' => $vendorDir . '/phing/phing/classes/phing/types/Parameter.php',
    'Parameterizable' => $vendorDir . '/phing/phing/classes/phing/types/Parameterizable.php',
    'PatchTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/PatchTask.php',
    'Path' => $vendorDir . '/phing/phing/classes/phing/types/Path.php',
    'PathConvert' => $vendorDir . '/phing/phing/classes/phing/tasks/system/PathConvert.php',
    'PathElement' => $vendorDir . '/phing/phing/classes/phing/types/Path.php',
    'PathToFileSet' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/property/PathToFileSet.php',
    'PathTokenizer' => $vendorDir . '/phing/phing/classes/phing/util/PathTokenizer.php',
    'PatternSet' => $vendorDir . '/phing/phing/classes/phing/types/PatternSet.php',
    'PatternSetNameEntry' => $vendorDir . '/phing/phing/classes/phing/types/PatternSet.php',
    'PearLogListener' => $vendorDir . '/phing/phing/classes/phing/listener/PearLogListener.php',
    'PearPackage2Task' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/PearPackage2Task.php',
    'PearPackageFileSet' => $vendorDir . '/phing/phing/classes/phing/types/PearPackageFileSet.php',
    'PearPackageScanner' => $vendorDir . '/phing/phing/classes/phing/util/PearPackageScanner.php',
    'PearPackageTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/PearPackageTask.php',
    'PearPkgMapping' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/PearPackageTask.php',
    'PearPkgMappingElement' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/PearPackageTask.php',
    'PearPkgOption' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/PearPackageTask.php',
    'PearPkgRole' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/PearPackageTask.php',
    'PgsqlPDOQuerySplitter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/pdo/PgsqlPDOQuerySplitter.php',
    'PharDataTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phar/PharDataTask.php',
    'PharMetadata' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phar/PharMetadata.php',
    'PharMetadataElement' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phar/PharMetadataElement.php',
    'PharPackageTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phar/PharPackageTask.php',
    'Phing' => $vendorDir . '/phing/phing/classes/phing/Phing.php',
    'PhingCallTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/PhingCallTask.php',
    'PhingFile' => $vendorDir . '/phing/phing/classes/phing/system/io/PhingFile.php',
    'PhingFilterReader' => $vendorDir . '/phing/phing/classes/phing/types/PhingFilterReader.php',
    'PhingPhpDocumentorErrorTracker' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpdoc/PhingPhpDocumentorErrorTracker.php',
    'PhingPhpDocumentorSetup' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpdoc/PhingPhpDocumentorSetup.php',
    'PhingReference' => $vendorDir . '/phing/phing/classes/phing/tasks/system/PhingTask.php',
    'PhingTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/PhingTask.php',
    'PhingVersion' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/PhingVersion.php',
    'PhingXMLContext' => $vendorDir . '/phing/phing/classes/phing/parser/PhingXMLContext.php',
    'PhkPackageTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phk/PhkPackageTask.php',
    'PhkPackageWebAccess' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phk/PhkPackageWebAccess.php',
    'PhkPackageWebAccessPath' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phk/PhkPackageWebAccessPath.php',
    'PhpArrayMapLines' => $vendorDir . '/phing/phing/classes/phing/filters/PhpArrayMapLines.php',
    'PhpCodeSnifferTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/PhpCodeSnifferTask.php',
    'PhpCodeSnifferTask_FormatterElement' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/PhpCodeSnifferTask.php',
    'PhpCodeSnifferTask_Wrapper' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpcs/PhpCodeSnifferTask_Wrapper.php',
    'PhpDependAnalyzerElement' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/pdepend/PhpDependAnalyzerElement.php',
    'PhpDependLoggerElement' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/pdepend/PhpDependLoggerElement.php',
    'PhpDependTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/pdepend/PhpDependTask.php',
    'PhpDocumentor2Task' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentor2Task.php',
    'PhpDocumentor2Wrapper' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentor2Wrapper.php',
    'PhpDocumentorExternalTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentorExternalTask.php',
    'PhpDocumentorTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentorTask.php',
    'PhpEvalTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/PhpEvalTask.php',
    'PhpLintTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/PhpLintTask.php',
    'PlainPDOResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/pdo/PlainPDOResultFormatter.php',
    'PlainPHPUnitResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpunit/formatter/PlainPHPUnitResultFormatter.php',
    'PrefixLines' => $vendorDir . '/phing/phing/classes/phing/filters/PrefixLines.php',
    'PregEngine' => $vendorDir . '/phing/phing/classes/phing/util/regexp/PregEngine.php',
    'PresentSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/PresentSelector.php',
    'PrintStream' => $vendorDir . '/phing/phing/classes/phing/system/io/PrintStream.php',
    'ProfileLogger' => $vendorDir . '/phing/phing/classes/phing/listener/ProfileLogger.php',
    'Project' => $vendorDir . '/phing/phing/classes/phing/Project.php',
    'ProjectComponent' => $vendorDir . '/phing/phing/classes/phing/ProjectComponent.php',
    'ProjectConfigurator' => $vendorDir . '/phing/phing/classes/phing/parser/ProjectConfigurator.php',
    'ProjectHandler' => $vendorDir . '/phing/phing/classes/phing/parser/ProjectHandler.php',
    'Properties' => $vendorDir . '/phing/phing/classes/phing/system/util/Properties.php',
    'PropertyPromptTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/PropertyPromptTask.php',
    'PropertyTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/PropertyTask.php',
    'PropertyValue' => $vendorDir . '/phing/phing/classes/phing/types/PropertyValue.php',
    'ReadableSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/ReadableSelector.php',
    'Reader' => $vendorDir . '/phing/phing/classes/phing/system/io/Reader.php',
    'RecorderEntry' => $vendorDir . '/phing/phing/classes/phing/tasks/system/RecorderEntry.php',
    'RecorderTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/RecorderTask.php',
    'Reference' => $vendorDir . '/phing/phing/classes/phing/types/Reference.php',
    'ReferenceExistsCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/ReferenceExistsCondition.php',
    'ReflexiveTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/ReflexiveTask.php',
    'RegexTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/property/RegexTask.php',
    'Regexp' => $vendorDir . '/phing/phing/classes/phing/util/regexp/Regexp.php',
    'RegexpEngine' => $vendorDir . '/phing/phing/classes/phing/util/regexp/RegexpEngine.php',
    'RegexpMapper' => $vendorDir . '/phing/phing/classes/phing/mappers/RegexpMapper.php',
    'Register' => $vendorDir . '/phing/phing/classes/phing/system/util/Register.php',
    'RegisterSlot' => $vendorDir . '/phing/phing/classes/phing/system/util/Register.php',
    'RegularExpression' => $vendorDir . '/phing/phing/classes/phing/types/RegularExpression.php',
    'ReplaceRegexp' => $vendorDir . '/phing/phing/classes/phing/filters/ReplaceRegexp.php',
    'ReplaceRegexpTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/ReplaceRegexpTask.php',
    'ReplaceTokens' => $vendorDir . '/phing/phing/classes/phing/filters/ReplaceTokens.php',
    'ReplaceTokensWithFile' => $vendorDir . '/phing/phing/classes/phing/filters/ReplaceTokensWithFile.php',
    'ResolvePathTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/ResolvePathTask.php',
    'Retry' => $vendorDir . '/phing/phing/classes/phing/tasks/system/Retry.php',
    'RootHandler' => $vendorDir . '/phing/phing/classes/phing/parser/RootHandler.php',
    'RuntimeConfigurable' => $vendorDir . '/phing/phing/classes/phing/RuntimeConfigurable.php',
    'S3GetTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/Service/Amazon/S3/S3GetTask.php',
    'S3PutTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/Service/Amazon/S3/S3PutTask.php',
    'SQLExecTransaction' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/creole/CreoleSQLExecTask.php',
    'SassTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/SassTask.php',
    'ScpTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/ssh/ScpTask.php',
    'SebastianBergmann\\CodeCoverage\\CodeCoverage' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage.php',
    'SebastianBergmann\\CodeCoverage\\CoveredCodeNotExecutedException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/CoveredCodeNotExecutedException.php',
    'SebastianBergmann\\CodeCoverage\\Driver\\Driver' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/Driver.php',
    'SebastianBergmann\\CodeCoverage\\Driver\\HHVM' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/HHVM.php',
    'SebastianBergmann\\CodeCoverage\\Driver\\PHPDBG' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/PHPDBG.php',
    'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/Xdebug.php',
    'SebastianBergmann\\CodeCoverage\\Exception' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/Exception.php',
    'SebastianBergmann\\CodeCoverage\\Filter' => $vendorDir . '/phpunit/php-code-coverage/src/Filter.php',
    'SebastianBergmann\\CodeCoverage\\InvalidArgumentException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/InvalidArgumentException.php',
    'SebastianBergmann\\CodeCoverage\\MissingCoversAnnotationException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/MissingCoversAnnotationException.php',
    'SebastianBergmann\\CodeCoverage\\Node\\AbstractNode' => $vendorDir . '/phpunit/php-code-coverage/src/Node/AbstractNode.php',
    'SebastianBergmann\\CodeCoverage\\Node\\Builder' => $vendorDir . '/phpunit/php-code-coverage/src/Node/Builder.php',
    'SebastianBergmann\\CodeCoverage\\Node\\Directory' => $vendorDir . '/phpunit/php-code-coverage/src/Node/Directory.php',
    'SebastianBergmann\\CodeCoverage\\Node\\File' => $vendorDir . '/phpunit/php-code-coverage/src/Node/File.php',
    'SebastianBergmann\\CodeCoverage\\Node\\Iterator' => $vendorDir . '/phpunit/php-code-coverage/src/Node/Iterator.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Clover' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Clover.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Crap4j' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Crap4j.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Html\\Dashboard' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Html\\Directory' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Html/Renderer/Directory.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Html\\Facade' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Html/Facade.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Html\\File' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Html/Renderer/File.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Html\\Renderer' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Html/Renderer.php',
    'SebastianBergmann\\CodeCoverage\\Report\\PHP' => $vendorDir . '/phpunit/php-code-coverage/src/Report/PHP.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Text' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Text.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Coverage' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Coverage.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Directory' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Directory.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Facade' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Facade.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Xml\\File' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/File.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Method' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Method.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Node' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Node.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Project' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Project.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Report' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Report.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Tests' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Tests.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Totals' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Totals.php',
    'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Unit' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Unit.php',
    'SebastianBergmann\\CodeCoverage\\RuntimeException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/RuntimeException.php',
    'SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php',
    'SebastianBergmann\\CodeCoverage\\Util' => $vendorDir . '/phpunit/php-code-coverage/src/Util.php',
    'SebastianBergmann\\CodeUnitReverseLookup\\Wizard' => $vendorDir . '/sebastian/code-unit-reverse-lookup/src/Wizard.php',
    'SebastianBergmann\\Comparator\\ArrayComparator' => $vendorDir . '/sebastian/comparator/src/ArrayComparator.php',
    'SebastianBergmann\\Comparator\\Comparator' => $vendorDir . '/sebastian/comparator/src/Comparator.php',
    'SebastianBergmann\\Comparator\\ComparisonFailure' => $vendorDir . '/sebastian/comparator/src/ComparisonFailure.php',
    'SebastianBergmann\\Comparator\\DOMNodeComparator' => $vendorDir . '/sebastian/comparator/src/DOMNodeComparator.php',
    'SebastianBergmann\\Comparator\\DateTimeComparator' => $vendorDir . '/sebastian/comparator/src/DateTimeComparator.php',
    'SebastianBergmann\\Comparator\\DoubleComparator' => $vendorDir . '/sebastian/comparator/src/DoubleComparator.php',
    'SebastianBergmann\\Comparator\\ExceptionComparator' => $vendorDir . '/sebastian/comparator/src/ExceptionComparator.php',
    'SebastianBergmann\\Comparator\\Factory' => $vendorDir . '/sebastian/comparator/src/Factory.php',
    'SebastianBergmann\\Comparator\\MockObjectComparator' => $vendorDir . '/sebastian/comparator/src/MockObjectComparator.php',
    'SebastianBergmann\\Comparator\\NumericComparator' => $vendorDir . '/sebastian/comparator/src/NumericComparator.php',
    'SebastianBergmann\\Comparator\\ObjectComparator' => $vendorDir . '/sebastian/comparator/src/ObjectComparator.php',
    'SebastianBergmann\\Comparator\\ResourceComparator' => $vendorDir . '/sebastian/comparator/src/ResourceComparator.php',
    'SebastianBergmann\\Comparator\\ScalarComparator' => $vendorDir . '/sebastian/comparator/src/ScalarComparator.php',
    'SebastianBergmann\\Comparator\\SplObjectStorageComparator' => $vendorDir . '/sebastian/comparator/src/SplObjectStorageComparator.php',
    'SebastianBergmann\\Comparator\\TypeComparator' => $vendorDir . '/sebastian/comparator/src/TypeComparator.php',
    'SebastianBergmann\\Diff\\Chunk' => $vendorDir . '/sebastian/diff/src/Chunk.php',
    'SebastianBergmann\\Diff\\Diff' => $vendorDir . '/sebastian/diff/src/Diff.php',
    'SebastianBergmann\\Diff\\Differ' => $vendorDir . '/sebastian/diff/src/Differ.php',
    'SebastianBergmann\\Diff\\LCS\\LongestCommonSubsequence' => $vendorDir . '/sebastian/diff/src/LCS/LongestCommonSubsequence.php',
    'SebastianBergmann\\Diff\\LCS\\MemoryEfficientImplementation' => $vendorDir . '/sebastian/diff/src/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php',
    'SebastianBergmann\\Diff\\LCS\\TimeEfficientImplementation' => $vendorDir . '/sebastian/diff/src/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php',
    'SebastianBergmann\\Diff\\Line' => $vendorDir . '/sebastian/diff/src/Line.php',
    'SebastianBergmann\\Diff\\Parser' => $vendorDir . '/sebastian/diff/src/Parser.php',
    'SebastianBergmann\\Environment\\Console' => $vendorDir . '/sebastian/environment/src/Console.php',
    'SebastianBergmann\\Environment\\Runtime' => $vendorDir . '/sebastian/environment/src/Runtime.php',
    'SebastianBergmann\\Exporter\\Exporter' => $vendorDir . '/sebastian/exporter/src/Exporter.php',
    'SebastianBergmann\\GlobalState\\Blacklist' => $vendorDir . '/sebastian/global-state/src/Blacklist.php',
    'SebastianBergmann\\GlobalState\\CodeExporter' => $vendorDir . '/sebastian/global-state/src/CodeExporter.php',
    'SebastianBergmann\\GlobalState\\Exception' => $vendorDir . '/sebastian/global-state/src/Exception.php',
    'SebastianBergmann\\GlobalState\\Restorer' => $vendorDir . '/sebastian/global-state/src/Restorer.php',
    'SebastianBergmann\\GlobalState\\RuntimeException' => $vendorDir . '/sebastian/global-state/src/RuntimeException.php',
    'SebastianBergmann\\GlobalState\\Snapshot' => $vendorDir . '/sebastian/global-state/src/Snapshot.php',
    'SebastianBergmann\\ObjectEnumerator\\Enumerator' => $vendorDir . '/sebastian/object-enumerator/src/Enumerator.php',
    'SebastianBergmann\\ObjectEnumerator\\Exception' => $vendorDir . '/sebastian/object-enumerator/src/Exception.php',
    'SebastianBergmann\\ObjectEnumerator\\InvalidArgumentException' => $vendorDir . '/sebastian/object-enumerator/src/InvalidArgumentException.php',
    'SebastianBergmann\\RecursionContext\\Context' => $vendorDir . '/sebastian/recursion-context/src/Context.php',
    'SebastianBergmann\\RecursionContext\\Exception' => $vendorDir . '/sebastian/recursion-context/src/Exception.php',
    'SebastianBergmann\\RecursionContext\\InvalidArgumentException' => $vendorDir . '/sebastian/recursion-context/src/InvalidArgumentException.php',
    'SebastianBergmann\\ResourceOperations\\ResourceOperations' => $vendorDir . '/sebastian/resource-operations/src/ResourceOperations.php',
    'SebastianBergmann\\Version' => $vendorDir . '/sebastian/version/src/Version.php',
    'SecurityException' => $vendorDir . '/phing/phing/classes/phing/system/lang/SecurityException.php',
    'SelectSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/SelectSelector.php',
    'SelectorContainer' => $vendorDir . '/phing/phing/classes/phing/types/selectors/SelectorContainer.php',
    'SelectorScanner' => $vendorDir . '/phing/phing/classes/phing/types/selectors/SelectorScanner.php',
    'SelectorUtils' => $vendorDir . '/phing/phing/classes/phing/types/selectors/SelectorUtils.php',
    'SequentialTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/SequentialTask.php',
    'Service_Amazon' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/Service/Amazon.php',
    'Service_Amazon_S3' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/Service/Amazon/S3.php',
    'SilentLogger' => $vendorDir . '/phing/phing/classes/phing/listener/SilentLogger.php',
    'SimpleTestCountResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestCountResultFormatter.php',
    'SimpleTestDebugResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestDebugResultFormatter.php',
    'SimpleTestFormatterElement' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestFormatterElement.php',
    'SimpleTestPlainResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestPlainResultFormatter.php',
    'SimpleTestResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestResultFormatter.php',
    'SimpleTestSummaryResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestSummaryResultFormatter.php',
    'SimpleTestTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestTask.php',
    'SimpleTestXmlResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestXmlResultFormatter.php',
    'SizeSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/SizeSelector.php',
    'SleepTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/SleepTask.php',
    'SmartyTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/SmartyTask.php',
    'SocketCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/SocketCondition.php',
    'SonarConfigurationFileParser' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/sonar/SonarConfigurationFileParser.php',
    'SonarProperty' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/sonar/SonarProperty.php',
    'SonarTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/sonar/SonarTask.php',
    'SortFilter' => $vendorDir . '/phing/phing/classes/phing/filters/SortFilter.php',
    'SourceFileScanner' => $vendorDir . '/phing/phing/classes/phing/util/SourceFileScanner.php',
    'Ssh2MethodConnectionParam' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/ssh/Ssh2MethodConnectionParam.php',
    'Ssh2MethodParam' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/ssh/Ssh2MethodParam.php',
    'SshTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/ssh/SshTask.php',
    'StopwatchTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/StopwatchTask.php',
    'StreamRequiredBuildLogger' => $vendorDir . '/phing/phing/classes/phing/listener/StreamRequiredBuildLogger.php',
    'StringHelper' => $vendorDir . '/phing/phing/classes/phing/util/StringHelper.php',
    'StringReader' => $vendorDir . '/phing/phing/classes/phing/system/io/StringReader.php',
    'StripLineBreaks' => $vendorDir . '/phing/phing/classes/phing/filters/StripLineBreaks.php',
    'StripLineComments' => $vendorDir . '/phing/phing/classes/phing/filters/StripLineComments.php',
    'StripPhpComments' => $vendorDir . '/phing/phing/classes/phing/filters/StripPhpComments.php',
    'StripWhitespace' => $vendorDir . '/phing/phing/classes/phing/filters/StripWhitespace.php',
    'SubBuildListener' => $vendorDir . '/phing/phing/classes/phing/SubBuildListener.php',
    'SuffixLines' => $vendorDir . '/phing/phing/classes/phing/filters/SuffixLines.php',
    'SummaryPHPUnitResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpunit/formatter/SummaryPHPUnitResultFormatter.php',
    'SvnBaseTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/svn/SvnBaseTask.php',
    'SvnCheckoutTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/svn/SvnCheckoutTask.php',
    'SvnCommitTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/svn/SvnCommitTask.php',
    'SvnCopyTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/svn/SvnCopyTask.php',
    'SvnExportTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/svn/SvnExportTask.php',
    'SvnInfoTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/svn/SvnInfoTask.php',
    'SvnLastRevisionTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/svn/SvnLastRevisionTask.php',
    'SvnListTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/svn/SvnListTask.php',
    'SvnLogTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/svn/SvnLogTask.php',
    'SvnSwitchTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/svn/SvnSwitchTask.php',
    'SvnUpdateTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/svn/SvnUpdateTask.php',
    'SwitchTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/SwitchTask.php',
    'SymfonyConsoleTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/SymfonyConsole/SymfonyConsoleTask.php',
    'SymlinkTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/SymlinkTask.php',
    'TabToSpaces' => $vendorDir . '/phing/phing/classes/phing/filters/TabToSpaces.php',
    'TailFilter' => $vendorDir . '/phing/phing/classes/phing/filters/TailFilter.php',
    'TarFileSet' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/TarTask.php',
    'TarTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/TarTask.php',
    'Target' => $vendorDir . '/phing/phing/classes/phing/Target.php',
    'TargetHandler' => $vendorDir . '/phing/phing/classes/phing/parser/TargetHandler.php',
    'TargetLogger' => $vendorDir . '/phing/phing/classes/phing/listener/TargetLogger.php',
    'Task' => $vendorDir . '/phing/phing/classes/phing/Task.php',
    'TaskAdapter' => $vendorDir . '/phing/phing/classes/phing/TaskAdapter.php',
    'TaskContainer' => $vendorDir . '/phing/phing/classes/phing/TaskContainer.php',
    'TaskdefTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/TaskdefTask.php',
    'TempFile' => $vendorDir . '/phing/phing/classes/phing/tasks/system/TempFile.php',
    'TextElement' => $vendorDir . '/phing/phing/classes/phing/tasks/system/AppendTask/TextElement.php',
    'Text_Template' => $vendorDir . '/phpunit/php-text-template/src/Template.php',
    'ThrowTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/ThrowTask.php',
    'TidyFilter' => $vendorDir . '/phing/phing/classes/phing/filters/TidyFilter.php',
    'Timer' => $vendorDir . '/phing/phing/classes/phing/system/util/Timer.php',
    'TimestampedLogger' => $vendorDir . '/phing/phing/classes/phing/listener/TimestampedLogger.php',
    'Token' => $vendorDir . '/phing/phing/classes/phing/filters/ReplaceTokens.php',
    'TokenReader' => $vendorDir . '/phing/phing/classes/phing/types/TokenReader.php',
    'TokenSource' => $vendorDir . '/phing/phing/classes/phing/types/TokenSource.php',
    'TouchTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/TouchTask.php',
    'TranslateGettext' => $vendorDir . '/phing/phing/classes/phing/filters/TranslateGettext.php',
    'TruncateTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/TruncateTask.php',
    'TryCatchTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/TryCatchTask.php',
    'TstampCustomFormat' => $vendorDir . '/phing/phing/classes/phing/tasks/system/TstampTask.php',
    'TstampTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/TstampTask.php',
    'TypeSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/TypeSelector.php',
    'TypedefTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/TypedefTask.php',
    'UnixFileSystem' => $vendorDir . '/phing/phing/classes/phing/system/io/UnixFileSystem.php',
    'UnknownElement' => $vendorDir . '/phing/phing/classes/phing/UnknownElement.php',
    'UntarTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/UntarTask.php',
    'UnzipTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/UnzipTask.php',
    'UpToDateTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/UpToDateTask.php',
    'VersionCompareCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/VersionCompareCondition.php',
    'VersionTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/VersionTask.php',
    'WaitForTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/WaitForTask.php',
    'WarnTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/WarnTask.php',
    'WikiPublishTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/WikiPublishTask.php',
    'Win32FileSystem' => $vendorDir . '/phing/phing/classes/phing/system/io/Win32FileSystem.php',
    'WinNTFileSystem' => $vendorDir . '/phing/phing/classes/phing/system/io/WinNTFileSystem.php',
    'WritableSelector' => $vendorDir . '/phing/phing/classes/phing/types/selectors/WritableSelector.php',
    'Writer' => $vendorDir . '/phing/phing/classes/phing/system/io/Writer.php',
    'XMLPDOResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/pdo/XMLPDOResultFormatter.php',
    'XMLPHPUnitResultFormatter' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/phpunit/formatter/XMLPHPUnitResultFormatter.php',
    'XSLTParam' => $vendorDir . '/phing/phing/classes/phing/filters/XsltFilter.php',
    'XincludeFilter' => $vendorDir . '/phing/phing/classes/phing/filters/XincludeFilter.php',
    'XmlLintTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/XmlLintTask.php',
    'XmlLogger' => $vendorDir . '/phing/phing/classes/phing/listener/XmlLogger.php',
    'XmlPropertyTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/XmlPropertyTask.php',
    'XorCondition' => $vendorDir . '/phing/phing/classes/phing/tasks/system/condition/XorCondition.php',
    'XsltFilter' => $vendorDir . '/phing/phing/classes/phing/filters/XsltFilter.php',
    'XsltTask' => $vendorDir . '/phing/phing/classes/phing/tasks/system/XsltTask.php',
    'YamlFileParser' => $vendorDir . '/phing/phing/classes/phing/system/io/YamlFileParser.php',
    'YesNoInputRequest' => $vendorDir . '/phing/phing/classes/phing/input/YesNoInputRequest.php',
    'ZendCodeAnalyzerTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/ZendCodeAnalyzerTask.php',
    'ZendGuardEncodeTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/zendguard/ZendGuardEncodeTask.php',
    'ZendGuardFileSet' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/zendguard/ZendGuardEncodeTask.php',
    'ZendGuardLicenseTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/zendguard/ZendGuardLicenseTask.php',
    'ZipFileSet' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/ZipTask.php',
    'ZipTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/ZipTask.php',
    'rSTTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/rSTTask.php',
    'zsdtBaseTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/zendserverdeploymenttool/zsdtBaseTask.php',
    'zsdtPackTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/zendserverdeploymenttool/zsdtPackTask.php',
    'zsdtValidateTask' => $vendorDir . '/phing/phing/classes/phing/tasks/ext/zendserverdeploymenttool/zsdtValidateTask.php',
);
<?php

// autoload_namespaces.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'Prophecy\\' => array($vendorDir . '/phpspec/prophecy/src'),
    'Guzzle\\Tests' => array($vendorDir . '/guzzle/guzzle/tests'),
    'Guzzle' => array($vendorDir . '/guzzle/guzzle/src'),
);
<?php

// autoload_psr4.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'phpDocumentor\\Reflection\\' => array($vendorDir . '/phpdocumentor/reflection-common/src', $vendorDir . '/phpdocumentor/type-resolver/src', $vendorDir . '/phpdocumentor/reflection-docblock/src'),
    'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'),
    'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'),
    'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
    'OpenTok\\' => array($baseDir . '/src/OpenTok'),
    'JohnStevenson\\JsonWorks\\' => array($vendorDir . '/johnstevenson/json-works/src'),
    'Firebase\\JWT\\' => array($vendorDir . '/firebase/php-jwt/src'),
    'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'),
    'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'),
);
<?php

// autoload_real.php @generated by Composer

class ComposerAutoloaderInit25d6eaeff17a07513aba0f926ea4d0e9
{
    private static $loader;

    public static function loadClassLoader($class)
    {
        if ('Composer\Autoload\ClassLoader' === $class) {
            require __DIR__ . '/ClassLoader.php';
        }
    }

    public static function getLoader()
    {
        if (null !== self::$loader) {
            return self::$loader;
        }

        spl_autoload_register(array('ComposerAutoloaderInit25d6eaeff17a07513aba0f926ea4d0e9', 'loadClassLoader'), true, true);
        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
        spl_autoload_unregister(array('ComposerAutoloaderInit25d6eaeff17a07513aba0f926ea4d0e9', 'loadClassLoader'));

        $includePaths = require __DIR__ . '/include_paths.php';
        array_push($includePaths, get_include_path());
        set_include_path(implode(PATH_SEPARATOR, $includePaths));

        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
        if ($useStaticLoader) {
            require_once __DIR__ . '/autoload_static.php';

            call_user_func(\Composer\Autoload\ComposerStaticInit25d6eaeff17a07513aba0f926ea4d0e9::getInitializer($loader));
        } else {
            $map = require __DIR__ . '/autoload_namespaces.php';
            foreach ($map as $namespace => $path) {
                $loader->set($namespace, $path);
            }

            $map = require __DIR__ . '/autoload_psr4.php';
            foreach ($map as $namespace => $path) {
                $loader->setPsr4($namespace, $path);
            }

            $classMap = require __DIR__ . '/autoload_classmap.php';
            if ($classMap) {
                $loader->addClassMap($classMap);
            }
        }

        $loader->register(true);

        return $loader;
    }
}
<?php

// autoload_static.php @generated by Composer

namespace Composer\Autoload;

class ComposerStaticInit25d6eaeff17a07513aba0f926ea4d0e9
{
    public static $prefixLengthsPsr4 = array (
        'p' => 
        array (
            'phpDocumentor\\Reflection\\' => 25,
        ),
        'W' => 
        array (
            'Webmozart\\Assert\\' => 17,
        ),
        'S' => 
        array (
            'Symfony\\Component\\Yaml\\' => 23,
            'Symfony\\Component\\EventDispatcher\\' => 34,
        ),
        'O' => 
        array (
            'OpenTok\\' => 8,
        ),
        'J' => 
        array (
            'JohnStevenson\\JsonWorks\\' => 24,
        ),
        'F' => 
        array (
            'Firebase\\JWT\\' => 13,
        ),
        'D' => 
        array (
            'Doctrine\\Instantiator\\' => 22,
            'DeepCopy\\' => 9,
        ),
    );

    public static $prefixDirsPsr4 = array (
        'phpDocumentor\\Reflection\\' => 
        array (
            0 => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src',
            1 => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src',
            2 => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src',
        ),
        'Webmozart\\Assert\\' => 
        array (
            0 => __DIR__ . '/..' . '/webmozart/assert/src',
        ),
        'Symfony\\Component\\Yaml\\' => 
        array (
            0 => __DIR__ . '/..' . '/symfony/yaml',
        ),
        'Symfony\\Component\\EventDispatcher\\' => 
        array (
            0 => __DIR__ . '/..' . '/symfony/event-dispatcher',
        ),
        'OpenTok\\' => 
        array (
            0 => __DIR__ . '/../..' . '/src/OpenTok',
        ),
        'JohnStevenson\\JsonWorks\\' => 
        array (
            0 => __DIR__ . '/..' . '/johnstevenson/json-works/src',
        ),
        'Firebase\\JWT\\' => 
        array (
            0 => __DIR__ . '/..' . '/firebase/php-jwt/src',
        ),
        'Doctrine\\Instantiator\\' => 
        array (
            0 => __DIR__ . '/..' . '/doctrine/instantiator/src/Doctrine/Instantiator',
        ),
        'DeepCopy\\' => 
        array (
            0 => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy',
        ),
    );

    public static $prefixesPsr0 = array (
        'P' => 
        array (
            'Prophecy\\' => 
            array (
                0 => __DIR__ . '/..' . '/phpspec/prophecy/src',
            ),
        ),
        'G' => 
        array (
            'Guzzle\\Tests' => 
            array (
                0 => __DIR__ . '/..' . '/guzzle/guzzle/tests',
            ),
            'Guzzle' => 
            array (
                0 => __DIR__ . '/..' . '/guzzle/guzzle/src',
            ),
        ),
    );

    public static $classMap = array (
        'AbstractFileSet' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/AbstractFileSet.php',
        'AbstractHandler' => __DIR__ . '/..' . '/phing/phing/classes/phing/parser/AbstractHandler.php',
        'AbstractLiquibaseTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/liquibase/AbstractLiquibaseTask.php',
        'AbstractPHPLocFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phploc/AbstractPHPLocFormatter.php',
        'AbstractPropertySetterTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/property/AbstractPropertySetterTask.php',
        'AbstractSAXParser' => __DIR__ . '/..' . '/phing/phing/classes/phing/parser/AbstractSAXParser.php',
        'AbstractSelectorContainer' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/AbstractSelectorContainer.php',
        'AdhocTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/AdhocTask.php',
        'AdhocTaskdefTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/AdhocTaskdefTask.php',
        'AdhocTypedefTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/AdhocTypedefTask.php',
        'AndCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/AndCondition.php',
        'AndSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/AndSelector.php',
        'AnsiColorLogger' => __DIR__ . '/..' . '/phing/phing/classes/phing/listener/AnsiColorLogger.php',
        'ApiGenTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/apigen/ApiGenTask.php',
        'AppendTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/AppendTask.php',
        'ApplyTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/ApplyTask.php',
        'Arg' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/SymfonyConsole/Arg.php',
        'AssignedVar' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/CapsuleTask.php',
        'AttribTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/AttribTask.php',
        'AutoloaderTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/AutoloaderTask.php',
        'AvailableTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/AvailableTask.php',
        'BaseExtendSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/BaseExtendSelector.php',
        'BaseFilterReader' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/BaseFilterReader.php',
        'BaseParamFilterReader' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/BaseParamFilterReader.php',
        'BaseSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/BaseSelector.php',
        'BaseSelectorContainer' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/BaseSelectorContainer.php',
        'Basename' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/Basename.php',
        'BatchTest' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpunit/BatchTest.php',
        'BlockForTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/BlockForTask.php',
        'BufferedReader' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/BufferedReader.php',
        'BufferedWriter' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/BufferedWriter.php',
        'BuildEvent' => __DIR__ . '/..' . '/phing/phing/classes/phing/BuildEvent.php',
        'BuildException' => __DIR__ . '/..' . '/phing/phing/classes/phing/BuildException.php',
        'BuildListener' => __DIR__ . '/..' . '/phing/phing/classes/phing/BuildListener.php',
        'BuildLogger' => __DIR__ . '/..' . '/phing/phing/classes/phing/BuildLogger.php',
        'BuildTimeoutException' => __DIR__ . '/..' . '/phing/phing/classes/phing/BuildTimeoutException.php',
        'Capsule' => __DIR__ . '/..' . '/phing/phing/classes/phing/lib/Capsule.php',
        'CapsuleTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/CapsuleTask.php',
        'CaseTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/SwitchTask.php',
        'ChainReaderHelper' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/util/ChainReaderHelper.php',
        'ChainableReader' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/ChainableReader.php',
        'ChainedMapper' => __DIR__ . '/..' . '/phing/phing/classes/phing/mappers/ChainedMapper.php',
        'Character' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/lang/Character.php',
        'ChmodTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/ChmodTask.php',
        'ChownTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/ChownTask.php',
        'CloverPHPUnitResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpunit/formatter/CloverPHPUnitResultFormatter.php',
        'Commandline' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/Commandline.php',
        'CommandlineArgument' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/Commandline.php',
        'CommandlineMarker' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/Commandline.php',
        'Comment' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/StripLineComments.php',
        'ComposerTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/ComposerTask.php',
        'CompositeMapper' => __DIR__ . '/..' . '/phing/phing/classes/phing/mappers/CompositeMapper.php',
        'ConcatFilter' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/ConcatFilter.php',
        'Condition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/Condition.php',
        'ConditionBase' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/ConditionBase.php',
        'ConditionEnumeration' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/ConditionBase.php',
        'ConditionTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/ConditionTask.php',
        'ConfigurationException' => __DIR__ . '/..' . '/phing/phing/classes/phing/ConfigurationException.php',
        'ConsoleReader' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/ConsoleReader.php',
        'ContainerMapper' => __DIR__ . '/..' . '/phing/phing/classes/phing/mappers/ContainerMapper.php',
        'Contains' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/LineContains.php',
        'ContainsCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/ContainsCondition.php',
        'ContainsRegexpSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/ContainsRegexpSelector.php',
        'ContainsSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/ContainsSelector.php',
        'CopyTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/CopyTask.php',
        'CoverageMerger' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/coverage/CoverageMerger.php',
        'CoverageMergerTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/coverage/CoverageMergerTask.php',
        'CoverageReportTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/coverage/CoverageReportTask.php',
        'CoverageReportTransformer' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/coverage/CoverageReportTransformer.php',
        'CoverageSetupTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/coverage/CoverageSetupTask.php',
        'CoverageThresholdTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/coverage/CoverageThresholdTask.php',
        'Crap4jPHPUnitResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpunit/formatter/Crap4jPHPUnitResultFormatter.php',
        'CreoleSQLExecTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/creole/CreoleSQLExecTask.php',
        'CreoleTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/creole/CreoleTask.php',
        'CustomChildCreator' => __DIR__ . '/..' . '/phing/phing/classes/phing/parser/CustomChildCreator.php',
        'CutDirsMapper' => __DIR__ . '/..' . '/phing/phing/classes/phing/mappers/CutDirsMapper.php',
        'CvsPassTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/CvsPassTask.php',
        'CvsTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/CvsTask.php',
        'DataStore' => __DIR__ . '/..' . '/phing/phing/classes/phing/util/DataStore.php',
        'DataType' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/DataType.php',
        'DataTypeHandler' => __DIR__ . '/..' . '/phing/phing/classes/phing/parser/DataTypeHandler.php',
        'DateSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/DateSelector.php',
        'DbDeployTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/dbdeploy/DbDeployTask.php',
        'DbmsSyntax' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntax.php',
        'DbmsSyntaxFactory' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxFactory.php',
        'DbmsSyntaxMsSql' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxMsSql.php',
        'DbmsSyntaxMysql' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxMysql.php',
        'DbmsSyntaxOracle' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxOracle.php',
        'DbmsSyntaxPgSQL' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxPgSQL.php',
        'DbmsSyntaxSQLite' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/dbdeploy/DbmsSyntaxSQLite.php',
        'DefaultInputHandler' => __DIR__ . '/..' . '/phing/phing/classes/phing/input/DefaultInputHandler.php',
        'DefaultLogger' => __DIR__ . '/..' . '/phing/phing/classes/phing/listener/DefaultLogger.php',
        'DefaultPDOQuerySplitter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/pdo/DefaultPDOQuerySplitter.php',
        'DefaultPHPCPDResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpcpd/formatter/DefaultPHPCPDResultFormatter.php',
        'DeleteTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/DeleteTask.php',
        'DependSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/DependSelector.php',
        'DepthSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/DepthSelector.php',
        'Description' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/Description.php',
        'Diagnostics' => __DIR__ . '/..' . '/phing/phing/classes/phing/Diagnostics.php',
        'DiagnosticsTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/DiagnosticsTask.php',
        'DifferentSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/DifferentSelector.php',
        'DirSet' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/DirSet.php',
        'DirectoryScanner' => __DIR__ . '/..' . '/phing/phing/classes/phing/util/DirectoryScanner.php',
        'Dirname' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/Dirname.php',
        'DocBlox_Parallel_Manager' => __DIR__ . '/..' . '/phing/phing/classes/phing/contrib/DocBlox/Parallel/Manager.php',
        'DocBlox_Parallel_Worker' => __DIR__ . '/..' . '/phing/phing/classes/phing/contrib/DocBlox/Parallel/Worker.php',
        'DocBlox_Parallel_WorkerPipe' => __DIR__ . '/..' . '/phing/phing/classes/phing/contrib/DocBlox/Parallel/WorkerPipe.php',
        'DummyPDOQuerySplitter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/pdo/DummyPDOQuerySplitter.php',
        'EchoProperties' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/EchoProperties.php',
        'EchoTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/EchoTask.php',
        'ElementHandler' => __DIR__ . '/..' . '/phing/phing/classes/phing/parser/ElementHandler.php',
        'ElseIfTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/IfTask.php',
        'EqualsCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/EqualsCondition.php',
        'EscapeUnicode' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/EscapeUnicode.php',
        'EventObject' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/lang/EventObject.php',
        'Excludes' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/Excludes.php',
        'ExcludesNameEntry' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/ExcludesNameEntry.php',
        'ExecTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/ExecTask.php',
        'ExitStatusException' => __DIR__ . '/..' . '/phing/phing/classes/phing/ExitStatusException.php',
        'ExpandProperties' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/ExpandProperties.php',
        'ExpatParseException' => __DIR__ . '/..' . '/phing/phing/classes/phing/parser/ExpatParseException.php',
        'ExpatParser' => __DIR__ . '/..' . '/phing/phing/classes/phing/parser/ExpatParser.php',
        'ExportPropertiesTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/ExportPropertiesTask.php',
        'ExtendFileSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/ExtendFileSelector.php',
        'ExtendSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/ExtendSelector.php',
        'ExtendedFileStream' => __DIR__ . '/..' . '/phing/phing/classes/phing/util/ExtendedFileStream.php',
        'ExtractBaseTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/ExtractBaseTask.php',
        'FailTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/FailTask.php',
        'FileHashTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/FileHashTask.php',
        'FileInputStream' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/FileInputStream.php',
        'FileList' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/FileList.php',
        'FileNameMapper' => __DIR__ . '/..' . '/phing/phing/classes/phing/mappers/FileNameMapper.php',
        'FileNotFoundException' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/lang/FileNotFoundException.php',
        'FileOutputStream' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/FileOutputStream.php',
        'FileParserFactory' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/FileParserFactory.php',
        'FileParserFactoryInterface' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/FileParserFactoryInterface.php',
        'FileParserInterface' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/FileParserInterface.php',
        'FileReader' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/FileReader.php',
        'FileSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/FileSelector.php',
        'FileSet' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/FileSet.php',
        'FileSizeTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/FileSizeTask.php',
        'FileSyncTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/FileSyncTask.php',
        'FileSystem' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/FileSystem.php',
        'FileUtils' => __DIR__ . '/..' . '/phing/phing/classes/phing/util/FileUtils.php',
        'FileWriter' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/FileWriter.php',
        'File_Iterator' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Iterator.php',
        'File_Iterator_Facade' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Facade.php',
        'File_Iterator_Factory' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Factory.php',
        'FilenameSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/FilenameSelector.php',
        'FilesMatch' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/FilesMatch.php',
        'FilterChain' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/FilterChain.php',
        'FilterReader' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/FilterReader.php',
        'FirstMatchMapper' => __DIR__ . '/..' . '/phing/phing/classes/phing/mappers/FirstMatchMapper.php',
        'FlattenMapper' => __DIR__ . '/..' . '/phing/phing/classes/phing/mappers/FlattenMapper.php',
        'ForeachTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/ForeachTask.php',
        'FormatterElement' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpunit/FormatterElement.php',
        'FtpDeployTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/FtpDeployTask.php',
        'FunctionParam' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/PhpEvalTask.php',
        'GitBaseTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/git/GitBaseTask.php',
        'GitBranchTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/git/GitBranchTask.php',
        'GitCheckoutTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/git/GitCheckoutTask.php',
        'GitCloneTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/git/GitCloneTask.php',
        'GitCommitTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/git/GitCommitTask.php',
        'GitDescribeTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/git/GitDescribeTask.php',
        'GitFetchTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/git/GitFetchTask.php',
        'GitGcTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/git/GitGcTask.php',
        'GitInitTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/git/GitInitTask.php',
        'GitLogTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/git/GitLogTask.php',
        'GitMergeTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/git/GitMergeTask.php',
        'GitPullTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/git/GitPullTask.php',
        'GitPushTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/git/GitPushTask.php',
        'GitTagTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/git/GitTagTask.php',
        'GlobMapper' => __DIR__ . '/..' . '/phing/phing/classes/phing/mappers/GlobMapper.php',
        'GrowlNotifyTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/GrowlNotifyTask.php',
        'HasFreeSpaceCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/HasFreeSpaceCondition.php',
        'HeadFilter' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/HeadFilter.php',
        'HgAddTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/hg/HgAddTask.php',
        'HgArchiveTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/hg/HgArchiveTask.php',
        'HgBaseTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/hg/HgBaseTask.php',
        'HgCloneTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/hg/HgCloneTask.php',
        'HgCommitTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/hg/HgCommitTask.php',
        'HgInitTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/hg/HgInitTask.php',
        'HgLogTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/hg/HgLogTask.php',
        'HgPullTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/hg/HgPullTask.php',
        'HgPushTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/hg/HgPushTask.php',
        'HgRevertTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/hg/HgRevertTask.php',
        'HgTagTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/hg/HgTagTask.php',
        'HgUpdateTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/hg/HgUpdateTask.php',
        'HtmlColorLogger' => __DIR__ . '/..' . '/phing/phing/classes/phing/listener/HtmlColorLogger.php',
        'HttpCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/HttpCondition.php',
        'HttpGetTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/HttpGetTask.php',
        'HttpRequestTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/HttpRequestTask.php',
        'HttpTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/HttpTask.php',
        'IOException' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/IOException.php',
        'IconvFilter' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/IconvFilter.php',
        'IdentityMapper' => __DIR__ . '/..' . '/phing/phing/classes/phing/mappers/IdentityMapper.php',
        'IfTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/IfTask.php',
        'ImportTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/ImportTask.php',
        'IncludePathTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/IncludePathTask.php',
        'IniFileConfig' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/inifile/IniFileConfig.php',
        'IniFileParser' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/IniFileParser.php',
        'IniFileRemove' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/inifile/IniFileRemove.php',
        'IniFileSet' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/inifile/IniFileSet.php',
        'IniFileTokenReader' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/util/IniFileTokenReader.php',
        'InifileTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/inifile/IniFileTask.php',
        'InputHandler' => __DIR__ . '/..' . '/phing/phing/classes/phing/input/InputHandler.php',
        'InputRequest' => __DIR__ . '/..' . '/phing/phing/classes/phing/input/InputRequest.php',
        'InputStream' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/InputStream.php',
        'InputStreamReader' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/InputStreamReader.php',
        'InputTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/InputTask.php',
        'IntrospectionHelper' => __DIR__ . '/..' . '/phing/phing/classes/phing/IntrospectionHelper.php',
        'IoncubeComment' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/ioncube/IoncubeComment.php',
        'IoncubeEncoderTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/ioncube/IoncubeEncoderTask.php',
        'IoncubeLicenseTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/ioncube/IoncubeLicenseTask.php',
        'IsFailure' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/IsFailure.php',
        'IsFalseCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/IsFalseCondition.php',
        'IsFileSelected' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/IsFileSelected.php',
        'IsPropertyFalseCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/IsPropertyFalseCondition.php',
        'IsPropertyTrueCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/IsPropertyTrueCondition.php',
        'IsSetCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/IsSetCondition.php',
        'IsTrueCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/IsTrueCondition.php',
        'IterableFileSet' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/IterableFileSet.php',
        'JsHintTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/JsHintTask.php',
        'JsMinTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/jsmin/JsMinTask.php',
        'JslLintTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/JslLintTask.php',
        'JsonLogger' => __DIR__ . '/..' . '/phing/phing/classes/phing/listener/JsonLogger.php',
        'LineContains' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/LineContains.php',
        'LineContainsRegexp' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/LineContainsRegexp.php',
        'LiquibaseChangeLogTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseChangeLogTask.php',
        'LiquibaseDbDocTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseDbDocTask.php',
        'LiquibaseDiffTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseDiffTask.php',
        'LiquibaseParameter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/liquibase/AbstractLiquibaseTask.php',
        'LiquibaseProperty' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/liquibase/AbstractLiquibaseTask.php',
        'LiquibaseRollbackTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseRollbackTask.php',
        'LiquibaseTagTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseTagTask.php',
        'LiquibaseTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseTask.php',
        'LiquibaseUpdateTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/liquibase/LiquibaseUpdateTask.php',
        'LoadFileTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/LoadFileTask.php',
        'Location' => __DIR__ . '/..' . '/phing/phing/classes/phing/parser/Location.php',
        'LogWriter' => __DIR__ . '/..' . '/phing/phing/classes/phing/util/LogWriter.php',
        'MailLogger' => __DIR__ . '/..' . '/phing/phing/classes/phing/listener/MailLogger.php',
        'MailTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/MailTask.php',
        'MajoritySelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/MajoritySelector.php',
        'ManifestTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/ManifestTask.php',
        'MapEntry' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/PathConvert.php',
        'Mapper' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/Mapper.php',
        'MappingSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/MappingSelector.php',
        'Matches' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/Matches.php',
        'MatchingTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/MatchingTask.php',
        'MergeMapper' => __DIR__ . '/..' . '/phing/phing/classes/phing/mappers/MergeMapper.php',
        'MkdirTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/MkdirTask.php',
        'MoveTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/MoveTask.php',
        'MultipleChoiceInputRequest' => __DIR__ . '/..' . '/phing/phing/classes/phing/input/MultipleChoiceInputRequest.php',
        'NestedCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/NestedCondition.php',
        'NoBannerLogger' => __DIR__ . '/..' . '/phing/phing/classes/phing/listener/NoBannerLogger.php',
        'NoneSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/NoneSelector.php',
        'NotCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/NotCondition.php',
        'NotSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/NotSelector.php',
        'NotifySendTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/NotifySendTask.php',
        'NullPointerException' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/lang/NullPointerException.php',
        'OrCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/OrCondition.php',
        'OrSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/OrSelector.php',
        'OsCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/OsCondition.php',
        'OutputStream' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/OutputStream.php',
        'OutputStreamWriter' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/OutputStreamWriter.php',
        'PDOQuerySplitter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/pdo/PDOQuerySplitter.php',
        'PDOResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/pdo/PDOResultFormatter.php',
        'PDOSQLExecFormatterElement' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/pdo/PDOSQLExecFormatterElement.php',
        'PDOSQLExecTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/pdo/PDOSQLExecTask.php',
        'PDOSQLExecTransaction' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/pdo/PDOSQLExecTask.php',
        'PDOTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/pdo/PDOTask.php',
        'PEAR_PackageFileManager_Fileset' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/pearpackage/Fileset.php',
        'PHPCPDFormatterElement' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpcpd/PHPCPDFormatterElement.php',
        'PHPCPDResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpcpd/formatter/PHPCPDResultFormatter.php',
        'PHPCPDTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpcpd/PHPCPDTask.php',
        'PHPLocCSVFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phploc/PHPLocCSVFormatter.php',
        'PHPLocFormatterElement' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phploc/PHPLocFormatterElement.php',
        'PHPLocFormatterFactory' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phploc/PHPLocFormatterFactory.php',
        'PHPLocTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phploc/PHPLocTask.php',
        'PHPLocTextFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phploc/PHPLocTextFormatter.php',
        'PHPLocXMLFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phploc/PHPLocXMLFormatter.php',
        'PHPMDFormatterElement' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpmd/PHPMDFormatterElement.php',
        'PHPMDRendererRemoveFromCache' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpmd/PHPMDRendererRemoveFromCache.php',
        'PHPMDTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpmd/PHPMDTask.php',
        'PHPUnitReportTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpunit/PHPUnitReportTask.php',
        'PHPUnitResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpunit/formatter/PHPUnitResultFormatter.php',
        'PHPUnitTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpunit/PHPUnitTask.php',
        'PHPUnitTestRunner' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpunit/PHPUnitTestRunner.php',
        'PHPUnitUtil' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpunit/PHPUnitUtil.php',
        'PHPUnit\\Framework\\Assert' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/Assert.php',
        'PHPUnit\\Framework\\AssertionFailedError' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/AssertionFailedError.php',
        'PHPUnit\\Framework\\BaseTestListener' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/BaseTestListener.php',
        'PHPUnit\\Framework\\Test' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/Test.php',
        'PHPUnit\\Framework\\TestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/TestCase.php',
        'PHPUnit\\Framework\\TestListener' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/TestListener.php',
        'PHPUnit\\Framework\\TestSuite' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/TestSuite.php',
        'PHPUnit_Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Exception.php',
        'PHPUnit_Extensions_GroupTestSuite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/GroupTestSuite.php',
        'PHPUnit_Extensions_PhptTestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/PhptTestCase.php',
        'PHPUnit_Extensions_PhptTestSuite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/PhptTestSuite.php',
        'PHPUnit_Extensions_RepeatedTest' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/RepeatedTest.php',
        'PHPUnit_Extensions_TestDecorator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/TestDecorator.php',
        'PHPUnit_Extensions_TicketListener' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/TicketListener.php',
        'PHPUnit_Framework_Assert' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Assert.php',
        'PHPUnit_Framework_AssertionFailedError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/AssertionFailedError.php',
        'PHPUnit_Framework_BaseTestListener' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/BaseTestListener.php',
        'PHPUnit_Framework_CodeCoverageException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/CodeCoverageException.php',
        'PHPUnit_Framework_Constraint' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint.php',
        'PHPUnit_Framework_Constraint_And' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/And.php',
        'PHPUnit_Framework_Constraint_ArrayHasKey' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ArrayHasKey.php',
        'PHPUnit_Framework_Constraint_ArraySubset' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ArraySubset.php',
        'PHPUnit_Framework_Constraint_Attribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Attribute.php',
        'PHPUnit_Framework_Constraint_Callback' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Callback.php',
        'PHPUnit_Framework_Constraint_ClassHasAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ClassHasAttribute.php',
        'PHPUnit_Framework_Constraint_ClassHasStaticAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ClassHasStaticAttribute.php',
        'PHPUnit_Framework_Constraint_Composite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Composite.php',
        'PHPUnit_Framework_Constraint_Count' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Count.php',
        'PHPUnit_Framework_Constraint_DirectoryExists' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/DirectoryExists.php',
        'PHPUnit_Framework_Constraint_Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Exception.php',
        'PHPUnit_Framework_Constraint_ExceptionCode' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ExceptionCode.php',
        'PHPUnit_Framework_Constraint_ExceptionMessage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessage.php',
        'PHPUnit_Framework_Constraint_ExceptionMessageRegExp' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessageRegExp.php',
        'PHPUnit_Framework_Constraint_FileExists' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/FileExists.php',
        'PHPUnit_Framework_Constraint_GreaterThan' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/GreaterThan.php',
        'PHPUnit_Framework_Constraint_IsAnything' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsAnything.php',
        'PHPUnit_Framework_Constraint_IsEmpty' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsEmpty.php',
        'PHPUnit_Framework_Constraint_IsEqual' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsEqual.php',
        'PHPUnit_Framework_Constraint_IsFalse' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsFalse.php',
        'PHPUnit_Framework_Constraint_IsFinite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsFinite.php',
        'PHPUnit_Framework_Constraint_IsIdentical' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsIdentical.php',
        'PHPUnit_Framework_Constraint_IsInfinite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsInfinite.php',
        'PHPUnit_Framework_Constraint_IsInstanceOf' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsInstanceOf.php',
        'PHPUnit_Framework_Constraint_IsJson' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsJson.php',
        'PHPUnit_Framework_Constraint_IsNan' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsNan.php',
        'PHPUnit_Framework_Constraint_IsNull' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsNull.php',
        'PHPUnit_Framework_Constraint_IsReadable' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsReadable.php',
        'PHPUnit_Framework_Constraint_IsTrue' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsTrue.php',
        'PHPUnit_Framework_Constraint_IsType' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsType.php',
        'PHPUnit_Framework_Constraint_IsWritable' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsWritable.php',
        'PHPUnit_Framework_Constraint_JsonMatches' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/JsonMatches.php',
        'PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/JsonMatches/ErrorMessageProvider.php',
        'PHPUnit_Framework_Constraint_LessThan' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/LessThan.php',
        'PHPUnit_Framework_Constraint_Not' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Not.php',
        'PHPUnit_Framework_Constraint_ObjectHasAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ObjectHasAttribute.php',
        'PHPUnit_Framework_Constraint_Or' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Or.php',
        'PHPUnit_Framework_Constraint_PCREMatch' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/PCREMatch.php',
        'PHPUnit_Framework_Constraint_SameSize' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/SameSize.php',
        'PHPUnit_Framework_Constraint_StringContains' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/StringContains.php',
        'PHPUnit_Framework_Constraint_StringEndsWith' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/StringEndsWith.php',
        'PHPUnit_Framework_Constraint_StringMatches' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/StringMatches.php',
        'PHPUnit_Framework_Constraint_StringStartsWith' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/StringStartsWith.php',
        'PHPUnit_Framework_Constraint_TraversableContains' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/TraversableContains.php',
        'PHPUnit_Framework_Constraint_TraversableContainsOnly' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/TraversableContainsOnly.php',
        'PHPUnit_Framework_Constraint_Xor' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Xor.php',
        'PHPUnit_Framework_CoveredCodeNotExecutedException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/CoveredCodeNotExecutedException.php',
        'PHPUnit_Framework_Error' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error.php',
        'PHPUnit_Framework_Error_Deprecated' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error/Deprecated.php',
        'PHPUnit_Framework_Error_Notice' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error/Notice.php',
        'PHPUnit_Framework_Error_Warning' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error/Warning.php',
        'PHPUnit_Framework_Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception.php',
        'PHPUnit_Framework_ExceptionWrapper' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/ExceptionWrapper.php',
        'PHPUnit_Framework_ExpectationFailedException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/ExpectationFailedException.php',
        'PHPUnit_Framework_IncompleteTest' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/IncompleteTest.php',
        'PHPUnit_Framework_IncompleteTestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/IncompleteTestCase.php',
        'PHPUnit_Framework_IncompleteTestError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/IncompleteTestError.php',
        'PHPUnit_Framework_InvalidCoversTargetException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/InvalidCoversTargetException.php',
        'PHPUnit_Framework_MissingCoversAnnotationException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MissingCoversAnnotationException.php',
        'PHPUnit_Framework_MockObject_BadMethodCallException' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/BadMethodCallException.php',
        'PHPUnit_Framework_MockObject_Builder_Identity' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Identity.php',
        'PHPUnit_Framework_MockObject_Builder_InvocationMocker' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/InvocationMocker.php',
        'PHPUnit_Framework_MockObject_Builder_Match' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Match.php',
        'PHPUnit_Framework_MockObject_Builder_MethodNameMatch' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/MethodNameMatch.php',
        'PHPUnit_Framework_MockObject_Builder_Namespace' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Namespace.php',
        'PHPUnit_Framework_MockObject_Builder_ParametersMatch' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/ParametersMatch.php',
        'PHPUnit_Framework_MockObject_Builder_Stub' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Stub.php',
        'PHPUnit_Framework_MockObject_Exception' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/Exception.php',
        'PHPUnit_Framework_MockObject_Generator' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Generator.php',
        'PHPUnit_Framework_MockObject_Invocation' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation.php',
        'PHPUnit_Framework_MockObject_InvocationMocker' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/InvocationMocker.php',
        'PHPUnit_Framework_MockObject_Invocation_Object' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation/Object.php',
        'PHPUnit_Framework_MockObject_Invocation_Static' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation/Static.php',
        'PHPUnit_Framework_MockObject_Invokable' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invokable.php',
        'PHPUnit_Framework_MockObject_Matcher' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher.php',
        'PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/AnyInvokedCount.php',
        'PHPUnit_Framework_MockObject_Matcher_AnyParameters' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/AnyParameters.php',
        'PHPUnit_Framework_MockObject_Matcher_ConsecutiveParameters' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/ConsecutiveParameters.php',
        'PHPUnit_Framework_MockObject_Matcher_Invocation' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/Invocation.php',
        'PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtIndex.php',
        'PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastCount' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtLeastCount.php',
        'PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtLeastOnce.php',
        'PHPUnit_Framework_MockObject_Matcher_InvokedAtMostCount' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtMostCount.php',
        'PHPUnit_Framework_MockObject_Matcher_InvokedCount' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedCount.php',
        'PHPUnit_Framework_MockObject_Matcher_InvokedRecorder' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedRecorder.php',
        'PHPUnit_Framework_MockObject_Matcher_MethodName' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/MethodName.php',
        'PHPUnit_Framework_MockObject_Matcher_Parameters' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/Parameters.php',
        'PHPUnit_Framework_MockObject_Matcher_StatelessInvocation' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/StatelessInvocation.php',
        'PHPUnit_Framework_MockObject_MockBuilder' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/MockBuilder.php',
        'PHPUnit_Framework_MockObject_MockObject' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/MockObject.php',
        'PHPUnit_Framework_MockObject_RuntimeException' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/RuntimeException.php',
        'PHPUnit_Framework_MockObject_Stub' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub.php',
        'PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ConsecutiveCalls.php',
        'PHPUnit_Framework_MockObject_Stub_Exception' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/Exception.php',
        'PHPUnit_Framework_MockObject_Stub_MatcherCollection' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/MatcherCollection.php',
        'PHPUnit_Framework_MockObject_Stub_Return' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/Return.php',
        'PHPUnit_Framework_MockObject_Stub_ReturnArgument' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnArgument.php',
        'PHPUnit_Framework_MockObject_Stub_ReturnCallback' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnCallback.php',
        'PHPUnit_Framework_MockObject_Stub_ReturnReference' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnReference.php',
        'PHPUnit_Framework_MockObject_Stub_ReturnSelf' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnSelf.php',
        'PHPUnit_Framework_MockObject_Stub_ReturnValueMap' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnValueMap.php',
        'PHPUnit_Framework_MockObject_Verifiable' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Verifiable.php',
        'PHPUnit_Framework_OutputError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/OutputError.php',
        'PHPUnit_Framework_RiskyTest' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/RiskyTest.php',
        'PHPUnit_Framework_RiskyTestError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/RiskyTestError.php',
        'PHPUnit_Framework_SelfDescribing' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SelfDescribing.php',
        'PHPUnit_Framework_SkippedTest' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SkippedTest.php',
        'PHPUnit_Framework_SkippedTestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SkippedTestCase.php',
        'PHPUnit_Framework_SkippedTestError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SkippedTestError.php',
        'PHPUnit_Framework_SkippedTestSuiteError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SkippedTestSuiteError.php',
        'PHPUnit_Framework_SyntheticError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SyntheticError.php',
        'PHPUnit_Framework_Test' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Test.php',
        'PHPUnit_Framework_TestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestCase.php',
        'PHPUnit_Framework_TestFailure' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestFailure.php',
        'PHPUnit_Framework_TestListener' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestListener.php',
        'PHPUnit_Framework_TestResult' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestResult.php',
        'PHPUnit_Framework_TestSuite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestSuite.php',
        'PHPUnit_Framework_TestSuite_DataProvider' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestSuite/DataProvider.php',
        'PHPUnit_Framework_UnintentionallyCoveredCodeError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/UnintentionallyCoveredCodeError.php',
        'PHPUnit_Framework_Warning' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Warning.php',
        'PHPUnit_Framework_WarningTestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/WarningTestCase.php',
        'PHPUnit_Runner_BaseTestRunner' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/BaseTestRunner.php',
        'PHPUnit_Runner_Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Exception.php',
        'PHPUnit_Runner_Filter_Factory' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/Factory.php',
        'PHPUnit_Runner_Filter_GroupFilterIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/Group.php',
        'PHPUnit_Runner_Filter_Group_Exclude' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/Group/Exclude.php',
        'PHPUnit_Runner_Filter_Group_Include' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/Group/Include.php',
        'PHPUnit_Runner_Filter_Test' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/Test.php',
        'PHPUnit_Runner_StandardTestSuiteLoader' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/StandardTestSuiteLoader.php',
        'PHPUnit_Runner_TestSuiteLoader' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/TestSuiteLoader.php',
        'PHPUnit_Runner_Version' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Version.php',
        'PHPUnit_TextUI_Command' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Command.php',
        'PHPUnit_TextUI_ResultPrinter' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/ResultPrinter.php',
        'PHPUnit_TextUI_TestRunner' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/TestRunner.php',
        'PHPUnit_Util_Blacklist' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Blacklist.php',
        'PHPUnit_Util_Configuration' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Configuration.php',
        'PHPUnit_Util_ConfigurationGenerator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/ConfigurationGenerator.php',
        'PHPUnit_Util_ErrorHandler' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/ErrorHandler.php',
        'PHPUnit_Util_Fileloader' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Fileloader.php',
        'PHPUnit_Util_Filesystem' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Filesystem.php',
        'PHPUnit_Util_Filter' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Filter.php',
        'PHPUnit_Util_Getopt' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Getopt.php',
        'PHPUnit_Util_GlobalState' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/GlobalState.php',
        'PHPUnit_Util_InvalidArgumentHelper' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/InvalidArgumentHelper.php',
        'PHPUnit_Util_Log_JSON' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Log/JSON.php',
        'PHPUnit_Util_Log_JUnit' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Log/JUnit.php',
        'PHPUnit_Util_Log_TAP' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Log/TAP.php',
        'PHPUnit_Util_Log_TeamCity' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Log/TeamCity.php',
        'PHPUnit_Util_PHP' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/PHP.php',
        'PHPUnit_Util_PHP_Default' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/PHP/Default.php',
        'PHPUnit_Util_PHP_Windows' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/PHP/Windows.php',
        'PHPUnit_Util_Printer' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Printer.php',
        'PHPUnit_Util_Regex' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Regex.php',
        'PHPUnit_Util_String' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/String.php',
        'PHPUnit_Util_Test' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Test.php',
        'PHPUnit_Util_TestDox_NamePrettifier' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/NamePrettifier.php',
        'PHPUnit_Util_TestDox_ResultPrinter' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter.php',
        'PHPUnit_Util_TestDox_ResultPrinter_HTML' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter/HTML.php',
        'PHPUnit_Util_TestDox_ResultPrinter_Text' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter/Text.php',
        'PHPUnit_Util_TestDox_ResultPrinter_XML' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter/XML.php',
        'PHPUnit_Util_TestSuiteIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestSuiteIterator.php',
        'PHPUnit_Util_Type' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Type.php',
        'PHPUnit_Util_XML' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/XML.php',
        'PHP_CodeSniffer_Reports_PhingRemoveFromCache' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpcs/Reports_PhingRemoveFromCache.php',
        'PHP_Timer' => __DIR__ . '/..' . '/phpunit/php-timer/src/Timer.php',
        'PHP_Token' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_TokenWithScope' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_TokenWithScopeAndVisibility' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ABSTRACT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_AMPERSAND' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_AND_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ARRAY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ARRAY_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_AS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ASYNC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_AT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_AWAIT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_BACKTICK' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_BAD_CHARACTER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_BOOLEAN_AND' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_BOOLEAN_OR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_BOOL_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_BREAK' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CALLABLE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CARET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CASE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CATCH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CHARACTER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CLASS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CLASS_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CLASS_NAME_CONSTANT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CLONE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CLOSE_BRACKET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CLOSE_CURLY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CLOSE_SQUARE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CLOSE_TAG' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_COALESCE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_COLON' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_COMMA' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_COMMENT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_COMPILER_HALT_OFFSET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CONCAT_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CONST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CONSTANT_ENCAPSED_STRING' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CONTINUE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_CURLY_OPEN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_DEC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_DECLARE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_DEFAULT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_DIR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_DIV' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_DIV_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_DNUMBER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_DO' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_DOC_COMMENT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_DOLLAR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_DOLLAR_OPEN_CURLY_BRACES' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_DOT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_DOUBLE_ARROW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_DOUBLE_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_DOUBLE_COLON' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_DOUBLE_QUOTES' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ECHO' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ELLIPSIS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ELSE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ELSEIF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_EMPTY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ENCAPSED_AND_WHITESPACE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ENDDECLARE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ENDFOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ENDFOREACH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ENDIF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ENDSWITCH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ENDWHILE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_END_HEREDOC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ENUM' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_EQUALS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_EVAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_EXCLAMATION_MARK' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_EXIT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_EXTENDS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_FILE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_FINAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_FINALLY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_FOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_FOREACH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_FUNCTION' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_FUNC_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_GLOBAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_GOTO' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_GT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_HALT_COMPILER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_IF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_IMPLEMENTS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_IN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_INC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_INCLUDE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_INCLUDE_ONCE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_INLINE_HTML' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_INSTANCEOF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_INSTEADOF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_INTERFACE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_INT_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ISSET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_IS_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_IS_GREATER_OR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_IS_IDENTICAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_IS_NOT_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_IS_NOT_IDENTICAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_IS_SMALLER_OR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_Includes' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_JOIN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_LAMBDA_ARROW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_LAMBDA_CP' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_LAMBDA_OP' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_LINE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_LIST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_LNUMBER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_LOGICAL_AND' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_LOGICAL_OR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_LOGICAL_XOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_LT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_METHOD_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_MINUS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_MINUS_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_MOD_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_MULT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_MUL_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_NAMESPACE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_NEW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_NS_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_NS_SEPARATOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_NULLSAFE_OBJECT_OPERATOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_NUM_STRING' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_OBJECT_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_OBJECT_OPERATOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_ONUMBER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_OPEN_BRACKET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_OPEN_CURLY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_OPEN_SQUARE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_OPEN_TAG' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_OPEN_TAG_WITH_ECHO' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_OR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_PAAMAYIM_NEKUDOTAYIM' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_PERCENT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_PIPE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_PLUS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_PLUS_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_POW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_POW_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_PRINT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_PRIVATE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_PROTECTED' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_PUBLIC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_QUESTION_MARK' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_REQUIRE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_REQUIRE_ONCE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_RETURN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_SEMICOLON' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_SHAPE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_SL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_SL_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_SPACESHIP' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_SR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_SR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_START_HEREDOC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_STATIC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_STRING' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_STRING_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_STRING_VARNAME' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_SUPER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_SWITCH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_Stream' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token/Stream.php',
        'PHP_Token_Stream_CachingFactory' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token/Stream/CachingFactory.php',
        'PHP_Token_THROW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_TILDE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_TRAIT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_TRAIT_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_TRY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_TYPE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_TYPELIST_GT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_TYPELIST_LT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_UNSET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_UNSET_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_USE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_USE_FUNCTION' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_VAR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_VARIABLE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_WHERE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_WHILE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_WHITESPACE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_XHP_ATTRIBUTE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_XHP_CATEGORY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_XHP_CATEGORY_LABEL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_XHP_CHILDREN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_XHP_LABEL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_XHP_REQUIRED' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_XHP_TAG_GT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_XHP_TAG_LT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_XHP_TEXT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_XOR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_YIELD' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PHP_Token_YIELD_FROM' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php',
        'PMDPHPCPDResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpcpd/formatter/PMDPHPCPDResultFormatter.php',
        'PackageAsPathTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/PackageAsPathTask.php',
        'ParallelTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/ParallelTask.php',
        'Parameter' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/Parameter.php',
        'Parameterizable' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/Parameterizable.php',
        'PatchTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/PatchTask.php',
        'Path' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/Path.php',
        'PathConvert' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/PathConvert.php',
        'PathElement' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/Path.php',
        'PathToFileSet' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/property/PathToFileSet.php',
        'PathTokenizer' => __DIR__ . '/..' . '/phing/phing/classes/phing/util/PathTokenizer.php',
        'PatternSet' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/PatternSet.php',
        'PatternSetNameEntry' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/PatternSet.php',
        'PearLogListener' => __DIR__ . '/..' . '/phing/phing/classes/phing/listener/PearLogListener.php',
        'PearPackage2Task' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/PearPackage2Task.php',
        'PearPackageFileSet' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/PearPackageFileSet.php',
        'PearPackageScanner' => __DIR__ . '/..' . '/phing/phing/classes/phing/util/PearPackageScanner.php',
        'PearPackageTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/PearPackageTask.php',
        'PearPkgMapping' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/PearPackageTask.php',
        'PearPkgMappingElement' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/PearPackageTask.php',
        'PearPkgOption' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/PearPackageTask.php',
        'PearPkgRole' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/PearPackageTask.php',
        'PgsqlPDOQuerySplitter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/pdo/PgsqlPDOQuerySplitter.php',
        'PharDataTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phar/PharDataTask.php',
        'PharMetadata' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phar/PharMetadata.php',
        'PharMetadataElement' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phar/PharMetadataElement.php',
        'PharPackageTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phar/PharPackageTask.php',
        'Phing' => __DIR__ . '/..' . '/phing/phing/classes/phing/Phing.php',
        'PhingCallTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/PhingCallTask.php',
        'PhingFile' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/PhingFile.php',
        'PhingFilterReader' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/PhingFilterReader.php',
        'PhingPhpDocumentorErrorTracker' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpdoc/PhingPhpDocumentorErrorTracker.php',
        'PhingPhpDocumentorSetup' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpdoc/PhingPhpDocumentorSetup.php',
        'PhingReference' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/PhingTask.php',
        'PhingTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/PhingTask.php',
        'PhingVersion' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/PhingVersion.php',
        'PhingXMLContext' => __DIR__ . '/..' . '/phing/phing/classes/phing/parser/PhingXMLContext.php',
        'PhkPackageTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phk/PhkPackageTask.php',
        'PhkPackageWebAccess' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phk/PhkPackageWebAccess.php',
        'PhkPackageWebAccessPath' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phk/PhkPackageWebAccessPath.php',
        'PhpArrayMapLines' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/PhpArrayMapLines.php',
        'PhpCodeSnifferTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/PhpCodeSnifferTask.php',
        'PhpCodeSnifferTask_FormatterElement' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/PhpCodeSnifferTask.php',
        'PhpCodeSnifferTask_Wrapper' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpcs/PhpCodeSnifferTask_Wrapper.php',
        'PhpDependAnalyzerElement' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/pdepend/PhpDependAnalyzerElement.php',
        'PhpDependLoggerElement' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/pdepend/PhpDependLoggerElement.php',
        'PhpDependTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/pdepend/PhpDependTask.php',
        'PhpDocumentor2Task' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentor2Task.php',
        'PhpDocumentor2Wrapper' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentor2Wrapper.php',
        'PhpDocumentorExternalTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentorExternalTask.php',
        'PhpDocumentorTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpdoc/PhpDocumentorTask.php',
        'PhpEvalTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/PhpEvalTask.php',
        'PhpLintTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/PhpLintTask.php',
        'PlainPDOResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/pdo/PlainPDOResultFormatter.php',
        'PlainPHPUnitResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpunit/formatter/PlainPHPUnitResultFormatter.php',
        'PrefixLines' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/PrefixLines.php',
        'PregEngine' => __DIR__ . '/..' . '/phing/phing/classes/phing/util/regexp/PregEngine.php',
        'PresentSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/PresentSelector.php',
        'PrintStream' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/PrintStream.php',
        'ProfileLogger' => __DIR__ . '/..' . '/phing/phing/classes/phing/listener/ProfileLogger.php',
        'Project' => __DIR__ . '/..' . '/phing/phing/classes/phing/Project.php',
        'ProjectComponent' => __DIR__ . '/..' . '/phing/phing/classes/phing/ProjectComponent.php',
        'ProjectConfigurator' => __DIR__ . '/..' . '/phing/phing/classes/phing/parser/ProjectConfigurator.php',
        'ProjectHandler' => __DIR__ . '/..' . '/phing/phing/classes/phing/parser/ProjectHandler.php',
        'Properties' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/util/Properties.php',
        'PropertyPromptTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/PropertyPromptTask.php',
        'PropertyTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/PropertyTask.php',
        'PropertyValue' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/PropertyValue.php',
        'ReadableSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/ReadableSelector.php',
        'Reader' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/Reader.php',
        'RecorderEntry' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/RecorderEntry.php',
        'RecorderTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/RecorderTask.php',
        'Reference' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/Reference.php',
        'ReferenceExistsCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/ReferenceExistsCondition.php',
        'ReflexiveTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/ReflexiveTask.php',
        'RegexTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/property/RegexTask.php',
        'Regexp' => __DIR__ . '/..' . '/phing/phing/classes/phing/util/regexp/Regexp.php',
        'RegexpEngine' => __DIR__ . '/..' . '/phing/phing/classes/phing/util/regexp/RegexpEngine.php',
        'RegexpMapper' => __DIR__ . '/..' . '/phing/phing/classes/phing/mappers/RegexpMapper.php',
        'Register' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/util/Register.php',
        'RegisterSlot' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/util/Register.php',
        'RegularExpression' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/RegularExpression.php',
        'ReplaceRegexp' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/ReplaceRegexp.php',
        'ReplaceRegexpTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/ReplaceRegexpTask.php',
        'ReplaceTokens' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/ReplaceTokens.php',
        'ReplaceTokensWithFile' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/ReplaceTokensWithFile.php',
        'ResolvePathTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/ResolvePathTask.php',
        'Retry' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/Retry.php',
        'RootHandler' => __DIR__ . '/..' . '/phing/phing/classes/phing/parser/RootHandler.php',
        'RuntimeConfigurable' => __DIR__ . '/..' . '/phing/phing/classes/phing/RuntimeConfigurable.php',
        'S3GetTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/Service/Amazon/S3/S3GetTask.php',
        'S3PutTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/Service/Amazon/S3/S3PutTask.php',
        'SQLExecTransaction' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/creole/CreoleSQLExecTask.php',
        'SassTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/SassTask.php',
        'ScpTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/ssh/ScpTask.php',
        'SebastianBergmann\\CodeCoverage\\CodeCoverage' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage.php',
        'SebastianBergmann\\CodeCoverage\\CoveredCodeNotExecutedException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/CoveredCodeNotExecutedException.php',
        'SebastianBergmann\\CodeCoverage\\Driver\\Driver' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/Driver.php',
        'SebastianBergmann\\CodeCoverage\\Driver\\HHVM' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/HHVM.php',
        'SebastianBergmann\\CodeCoverage\\Driver\\PHPDBG' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/PHPDBG.php',
        'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/Xdebug.php',
        'SebastianBergmann\\CodeCoverage\\Exception' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/Exception.php',
        'SebastianBergmann\\CodeCoverage\\Filter' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Filter.php',
        'SebastianBergmann\\CodeCoverage\\InvalidArgumentException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/InvalidArgumentException.php',
        'SebastianBergmann\\CodeCoverage\\MissingCoversAnnotationException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/MissingCoversAnnotationException.php',
        'SebastianBergmann\\CodeCoverage\\Node\\AbstractNode' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/AbstractNode.php',
        'SebastianBergmann\\CodeCoverage\\Node\\Builder' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/Builder.php',
        'SebastianBergmann\\CodeCoverage\\Node\\Directory' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/Directory.php',
        'SebastianBergmann\\CodeCoverage\\Node\\File' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/File.php',
        'SebastianBergmann\\CodeCoverage\\Node\\Iterator' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/Iterator.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Clover' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Clover.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Crap4j' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Crap4j.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Html\\Dashboard' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Html\\Directory' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Html/Renderer/Directory.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Html\\Facade' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Html/Facade.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Html\\File' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Html/Renderer/File.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Html\\Renderer' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Html/Renderer.php',
        'SebastianBergmann\\CodeCoverage\\Report\\PHP' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/PHP.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Text' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Text.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Coverage' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Coverage.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Directory' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Directory.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Facade' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Facade.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Xml\\File' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/File.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Method' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Method.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Node' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Node.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Project' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Project.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Report' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Report.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Tests' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Tests.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Totals' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Totals.php',
        'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Unit' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Unit.php',
        'SebastianBergmann\\CodeCoverage\\RuntimeException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/RuntimeException.php',
        'SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php',
        'SebastianBergmann\\CodeCoverage\\Util' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Util.php',
        'SebastianBergmann\\CodeUnitReverseLookup\\Wizard' => __DIR__ . '/..' . '/sebastian/code-unit-reverse-lookup/src/Wizard.php',
        'SebastianBergmann\\Comparator\\ArrayComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ArrayComparator.php',
        'SebastianBergmann\\Comparator\\Comparator' => __DIR__ . '/..' . '/sebastian/comparator/src/Comparator.php',
        'SebastianBergmann\\Comparator\\ComparisonFailure' => __DIR__ . '/..' . '/sebastian/comparator/src/ComparisonFailure.php',
        'SebastianBergmann\\Comparator\\DOMNodeComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/DOMNodeComparator.php',
        'SebastianBergmann\\Comparator\\DateTimeComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/DateTimeComparator.php',
        'SebastianBergmann\\Comparator\\DoubleComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/DoubleComparator.php',
        'SebastianBergmann\\Comparator\\ExceptionComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ExceptionComparator.php',
        'SebastianBergmann\\Comparator\\Factory' => __DIR__ . '/..' . '/sebastian/comparator/src/Factory.php',
        'SebastianBergmann\\Comparator\\MockObjectComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/MockObjectComparator.php',
        'SebastianBergmann\\Comparator\\NumericComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/NumericComparator.php',
        'SebastianBergmann\\Comparator\\ObjectComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ObjectComparator.php',
        'SebastianBergmann\\Comparator\\ResourceComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ResourceComparator.php',
        'SebastianBergmann\\Comparator\\ScalarComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ScalarComparator.php',
        'SebastianBergmann\\Comparator\\SplObjectStorageComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/SplObjectStorageComparator.php',
        'SebastianBergmann\\Comparator\\TypeComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/TypeComparator.php',
        'SebastianBergmann\\Diff\\Chunk' => __DIR__ . '/..' . '/sebastian/diff/src/Chunk.php',
        'SebastianBergmann\\Diff\\Diff' => __DIR__ . '/..' . '/sebastian/diff/src/Diff.php',
        'SebastianBergmann\\Diff\\Differ' => __DIR__ . '/..' . '/sebastian/diff/src/Differ.php',
        'SebastianBergmann\\Diff\\LCS\\LongestCommonSubsequence' => __DIR__ . '/..' . '/sebastian/diff/src/LCS/LongestCommonSubsequence.php',
        'SebastianBergmann\\Diff\\LCS\\MemoryEfficientImplementation' => __DIR__ . '/..' . '/sebastian/diff/src/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php',
        'SebastianBergmann\\Diff\\LCS\\TimeEfficientImplementation' => __DIR__ . '/..' . '/sebastian/diff/src/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php',
        'SebastianBergmann\\Diff\\Line' => __DIR__ . '/..' . '/sebastian/diff/src/Line.php',
        'SebastianBergmann\\Diff\\Parser' => __DIR__ . '/..' . '/sebastian/diff/src/Parser.php',
        'SebastianBergmann\\Environment\\Console' => __DIR__ . '/..' . '/sebastian/environment/src/Console.php',
        'SebastianBergmann\\Environment\\Runtime' => __DIR__ . '/..' . '/sebastian/environment/src/Runtime.php',
        'SebastianBergmann\\Exporter\\Exporter' => __DIR__ . '/..' . '/sebastian/exporter/src/Exporter.php',
        'SebastianBergmann\\GlobalState\\Blacklist' => __DIR__ . '/..' . '/sebastian/global-state/src/Blacklist.php',
        'SebastianBergmann\\GlobalState\\CodeExporter' => __DIR__ . '/..' . '/sebastian/global-state/src/CodeExporter.php',
        'SebastianBergmann\\GlobalState\\Exception' => __DIR__ . '/..' . '/sebastian/global-state/src/Exception.php',
        'SebastianBergmann\\GlobalState\\Restorer' => __DIR__ . '/..' . '/sebastian/global-state/src/Restorer.php',
        'SebastianBergmann\\GlobalState\\RuntimeException' => __DIR__ . '/..' . '/sebastian/global-state/src/RuntimeException.php',
        'SebastianBergmann\\GlobalState\\Snapshot' => __DIR__ . '/..' . '/sebastian/global-state/src/Snapshot.php',
        'SebastianBergmann\\ObjectEnumerator\\Enumerator' => __DIR__ . '/..' . '/sebastian/object-enumerator/src/Enumerator.php',
        'SebastianBergmann\\ObjectEnumerator\\Exception' => __DIR__ . '/..' . '/sebastian/object-enumerator/src/Exception.php',
        'SebastianBergmann\\ObjectEnumerator\\InvalidArgumentException' => __DIR__ . '/..' . '/sebastian/object-enumerator/src/InvalidArgumentException.php',
        'SebastianBergmann\\RecursionContext\\Context' => __DIR__ . '/..' . '/sebastian/recursion-context/src/Context.php',
        'SebastianBergmann\\RecursionContext\\Exception' => __DIR__ . '/..' . '/sebastian/recursion-context/src/Exception.php',
        'SebastianBergmann\\RecursionContext\\InvalidArgumentException' => __DIR__ . '/..' . '/sebastian/recursion-context/src/InvalidArgumentException.php',
        'SebastianBergmann\\ResourceOperations\\ResourceOperations' => __DIR__ . '/..' . '/sebastian/resource-operations/src/ResourceOperations.php',
        'SebastianBergmann\\Version' => __DIR__ . '/..' . '/sebastian/version/src/Version.php',
        'SecurityException' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/lang/SecurityException.php',
        'SelectSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/SelectSelector.php',
        'SelectorContainer' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/SelectorContainer.php',
        'SelectorScanner' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/SelectorScanner.php',
        'SelectorUtils' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/SelectorUtils.php',
        'SequentialTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/SequentialTask.php',
        'Service_Amazon' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/Service/Amazon.php',
        'Service_Amazon_S3' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/Service/Amazon/S3.php',
        'SilentLogger' => __DIR__ . '/..' . '/phing/phing/classes/phing/listener/SilentLogger.php',
        'SimpleTestCountResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestCountResultFormatter.php',
        'SimpleTestDebugResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestDebugResultFormatter.php',
        'SimpleTestFormatterElement' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestFormatterElement.php',
        'SimpleTestPlainResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestPlainResultFormatter.php',
        'SimpleTestResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestResultFormatter.php',
        'SimpleTestSummaryResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestSummaryResultFormatter.php',
        'SimpleTestTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestTask.php',
        'SimpleTestXmlResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/simpletest/SimpleTestXmlResultFormatter.php',
        'SizeSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/SizeSelector.php',
        'SleepTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/SleepTask.php',
        'SmartyTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/SmartyTask.php',
        'SocketCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/SocketCondition.php',
        'SonarConfigurationFileParser' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/sonar/SonarConfigurationFileParser.php',
        'SonarProperty' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/sonar/SonarProperty.php',
        'SonarTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/sonar/SonarTask.php',
        'SortFilter' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/SortFilter.php',
        'SourceFileScanner' => __DIR__ . '/..' . '/phing/phing/classes/phing/util/SourceFileScanner.php',
        'Ssh2MethodConnectionParam' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/ssh/Ssh2MethodConnectionParam.php',
        'Ssh2MethodParam' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/ssh/Ssh2MethodParam.php',
        'SshTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/ssh/SshTask.php',
        'StopwatchTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/StopwatchTask.php',
        'StreamRequiredBuildLogger' => __DIR__ . '/..' . '/phing/phing/classes/phing/listener/StreamRequiredBuildLogger.php',
        'StringHelper' => __DIR__ . '/..' . '/phing/phing/classes/phing/util/StringHelper.php',
        'StringReader' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/StringReader.php',
        'StripLineBreaks' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/StripLineBreaks.php',
        'StripLineComments' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/StripLineComments.php',
        'StripPhpComments' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/StripPhpComments.php',
        'StripWhitespace' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/StripWhitespace.php',
        'SubBuildListener' => __DIR__ . '/..' . '/phing/phing/classes/phing/SubBuildListener.php',
        'SuffixLines' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/SuffixLines.php',
        'SummaryPHPUnitResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpunit/formatter/SummaryPHPUnitResultFormatter.php',
        'SvnBaseTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/svn/SvnBaseTask.php',
        'SvnCheckoutTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/svn/SvnCheckoutTask.php',
        'SvnCommitTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/svn/SvnCommitTask.php',
        'SvnCopyTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/svn/SvnCopyTask.php',
        'SvnExportTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/svn/SvnExportTask.php',
        'SvnInfoTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/svn/SvnInfoTask.php',
        'SvnLastRevisionTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/svn/SvnLastRevisionTask.php',
        'SvnListTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/svn/SvnListTask.php',
        'SvnLogTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/svn/SvnLogTask.php',
        'SvnSwitchTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/svn/SvnSwitchTask.php',
        'SvnUpdateTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/svn/SvnUpdateTask.php',
        'SwitchTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/SwitchTask.php',
        'SymfonyConsoleTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/SymfonyConsole/SymfonyConsoleTask.php',
        'SymlinkTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/SymlinkTask.php',
        'TabToSpaces' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/TabToSpaces.php',
        'TailFilter' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/TailFilter.php',
        'TarFileSet' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/TarTask.php',
        'TarTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/TarTask.php',
        'Target' => __DIR__ . '/..' . '/phing/phing/classes/phing/Target.php',
        'TargetHandler' => __DIR__ . '/..' . '/phing/phing/classes/phing/parser/TargetHandler.php',
        'TargetLogger' => __DIR__ . '/..' . '/phing/phing/classes/phing/listener/TargetLogger.php',
        'Task' => __DIR__ . '/..' . '/phing/phing/classes/phing/Task.php',
        'TaskAdapter' => __DIR__ . '/..' . '/phing/phing/classes/phing/TaskAdapter.php',
        'TaskContainer' => __DIR__ . '/..' . '/phing/phing/classes/phing/TaskContainer.php',
        'TaskdefTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/TaskdefTask.php',
        'TempFile' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/TempFile.php',
        'TextElement' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/AppendTask/TextElement.php',
        'Text_Template' => __DIR__ . '/..' . '/phpunit/php-text-template/src/Template.php',
        'ThrowTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/ThrowTask.php',
        'TidyFilter' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/TidyFilter.php',
        'Timer' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/util/Timer.php',
        'TimestampedLogger' => __DIR__ . '/..' . '/phing/phing/classes/phing/listener/TimestampedLogger.php',
        'Token' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/ReplaceTokens.php',
        'TokenReader' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/TokenReader.php',
        'TokenSource' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/TokenSource.php',
        'TouchTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/TouchTask.php',
        'TranslateGettext' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/TranslateGettext.php',
        'TruncateTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/TruncateTask.php',
        'TryCatchTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/TryCatchTask.php',
        'TstampCustomFormat' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/TstampTask.php',
        'TstampTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/TstampTask.php',
        'TypeSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/TypeSelector.php',
        'TypedefTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/TypedefTask.php',
        'UnixFileSystem' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/UnixFileSystem.php',
        'UnknownElement' => __DIR__ . '/..' . '/phing/phing/classes/phing/UnknownElement.php',
        'UntarTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/UntarTask.php',
        'UnzipTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/UnzipTask.php',
        'UpToDateTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/UpToDateTask.php',
        'VersionCompareCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/VersionCompareCondition.php',
        'VersionTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/VersionTask.php',
        'WaitForTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/WaitForTask.php',
        'WarnTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/WarnTask.php',
        'WikiPublishTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/WikiPublishTask.php',
        'Win32FileSystem' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/Win32FileSystem.php',
        'WinNTFileSystem' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/WinNTFileSystem.php',
        'WritableSelector' => __DIR__ . '/..' . '/phing/phing/classes/phing/types/selectors/WritableSelector.php',
        'Writer' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/Writer.php',
        'XMLPDOResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/pdo/XMLPDOResultFormatter.php',
        'XMLPHPUnitResultFormatter' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/phpunit/formatter/XMLPHPUnitResultFormatter.php',
        'XSLTParam' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/XsltFilter.php',
        'XincludeFilter' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/XincludeFilter.php',
        'XmlLintTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/XmlLintTask.php',
        'XmlLogger' => __DIR__ . '/..' . '/phing/phing/classes/phing/listener/XmlLogger.php',
        'XmlPropertyTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/XmlPropertyTask.php',
        'XorCondition' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/condition/XorCondition.php',
        'XsltFilter' => __DIR__ . '/..' . '/phing/phing/classes/phing/filters/XsltFilter.php',
        'XsltTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/system/XsltTask.php',
        'YamlFileParser' => __DIR__ . '/..' . '/phing/phing/classes/phing/system/io/YamlFileParser.php',
        'YesNoInputRequest' => __DIR__ . '/..' . '/phing/phing/classes/phing/input/YesNoInputRequest.php',
        'ZendCodeAnalyzerTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/ZendCodeAnalyzerTask.php',
        'ZendGuardEncodeTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/zendguard/ZendGuardEncodeTask.php',
        'ZendGuardFileSet' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/zendguard/ZendGuardEncodeTask.php',
        'ZendGuardLicenseTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/zendguard/ZendGuardLicenseTask.php',
        'ZipFileSet' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/ZipTask.php',
        'ZipTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/ZipTask.php',
        'rSTTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/rSTTask.php',
        'zsdtBaseTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/zendserverdeploymenttool/zsdtBaseTask.php',
        'zsdtPackTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/zendserverdeploymenttool/zsdtPackTask.php',
        'zsdtValidateTask' => __DIR__ . '/..' . '/phing/phing/classes/phing/tasks/ext/zendserverdeploymenttool/zsdtValidateTask.php',
    );

    public static function getInitializer(ClassLoader $loader)
    {
        return \Closure::bind(function () use ($loader) {
            $loader->prefixLengthsPsr4 = ComposerStaticInit25d6eaeff17a07513aba0f926ea4d0e9::$prefixLengthsPsr4;
            $loader->prefixDirsPsr4 = ComposerStaticInit25d6eaeff17a07513aba0f926ea4d0e9::$prefixDirsPsr4;
            $loader->prefixesPsr0 = ComposerStaticInit25d6eaeff17a07513aba0f926ea4d0e9::$prefixesPsr0;
            $loader->classMap = ComposerStaticInit25d6eaeff17a07513aba0f926ea4d0e9::$classMap;

        }, null, ClassLoader::class);
    }
}
<?php

/*
 * This file is part of Composer.
 *
 * (c) Nils Adermann <naderman@naderman.de>
 *     Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Composer\Autoload;

/**
 * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
 *
 *     $loader = new \Composer\Autoload\ClassLoader();
 *
 *     // register classes with namespaces
 *     $loader->add('Symfony\Component', __DIR__.'/component');
 *     $loader->add('Symfony',           __DIR__.'/framework');
 *
 *     // activate the autoloader
 *     $loader->register();
 *
 *     // to enable searching the include path (eg. for PEAR packages)
 *     $loader->setUseIncludePath(true);
 *
 * In this example, if you try to use a class in the Symfony\Component
 * namespace or one of its children (Symfony\Component\Console for instance),
 * the autoloader will first look for the class under the component/
 * directory, and it will then fallback to the framework/ directory if not
 * found before giving up.
 *
 * This class is loosely based on the Symfony UniversalClassLoader.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Jordi Boggiano <j.boggiano@seld.be>
 * @see    http://www.php-fig.org/psr/psr-0/
 * @see    http://www.php-fig.org/psr/psr-4/
 */
class ClassLoader
{
    // PSR-4
    private $prefixLengthsPsr4 = array();
    private $prefixDirsPsr4 = array();
    private $fallbackDirsPsr4 = array();

    // PSR-0
    private $prefixesPsr0 = array();
    private $fallbackDirsPsr0 = array();

    private $useIncludePath = false;
    private $classMap = array();
    private $classMapAuthoritative = false;
    private $missingClasses = array();
    private $apcuPrefix;

    public function getPrefixes()
    {
        if (!empty($this->prefixesPsr0)) {
            return call_user_func_array('array_merge', $this->prefixesPsr0);
        }

        return array();
    }

    public function getPrefixesPsr4()
    {
        return $this->prefixDirsPsr4;
    }

    public function getFallbackDirs()
    {
        return $this->fallbackDirsPsr0;
    }

    public function getFallbackDirsPsr4()
    {
        return $this->fallbackDirsPsr4;
    }

    public function getClassMap()
    {
        return $this->classMap;
    }

    /**
     * @param array $classMap Class to filename map
     */
    public function addClassMap(array $classMap)
    {
        if ($this->classMap) {
            $this->classMap = array_merge($this->classMap, $classMap);
        } else {
            $this->classMap = $classMap;
        }
    }

    /**
     * Registers a set of PSR-0 directories for a given prefix, either
     * appending or prepending to the ones previously set for this prefix.
     *
     * @param string       $prefix  The prefix
     * @param array|string $paths   The PSR-0 root directories
     * @param bool         $prepend Whether to prepend the directories
     */
    public function add($prefix, $paths, $prepend = false)
    {
        if (!$prefix) {
            if ($prepend) {
                $this->fallbackDirsPsr0 = array_merge(
                    (array) $paths,
                    $this->fallbackDirsPsr0
                );
            } else {
                $this->fallbackDirsPsr0 = array_merge(
                    $this->fallbackDirsPsr0,
                    (array) $paths
                );
            }

            return;
        }

        $first = $prefix[0];
        if (!isset($this->prefixesPsr0[$first][$prefix])) {
            $this->prefixesPsr0[$first][$prefix] = (array) $paths;

            return;
        }
        if ($prepend) {
            $this->prefixesPsr0[$first][$prefix] = array_merge(
                (array) $paths,
                $this->prefixesPsr0[$first][$prefix]
            );
        } else {
            $this->prefixesPsr0[$first][$prefix] = array_merge(
                $this->prefixesPsr0[$first][$prefix],
                (array) $paths
            );
        }
    }

    /**
     * Registers a set of PSR-4 directories for a given namespace, either
     * appending or prepending to the ones previously set for this namespace.
     *
     * @param string       $prefix  The prefix/namespace, with trailing '\\'
     * @param array|string $paths   The PSR-4 base directories
     * @param bool         $prepend Whether to prepend the directories
     *
     * @throws \InvalidArgumentException
     */
    public function addPsr4($prefix, $paths, $prepend = false)
    {
        if (!$prefix) {
            // Register directories for the root namespace.
            if ($prepend) {
                $this->fallbackDirsPsr4 = array_merge(
                    (array) $paths,
                    $this->fallbackDirsPsr4
                );
            } else {
                $this->fallbackDirsPsr4 = array_merge(
                    $this->fallbackDirsPsr4,
                    (array) $paths
                );
            }
        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
            // Register directories for a new namespace.
            $length = strlen($prefix);
            if ('\\' !== $prefix[$length - 1]) {
                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
            }
            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
            $this->prefixDirsPsr4[$prefix] = (array) $paths;
        } elseif ($prepend) {
            // Prepend directories for an already registered namespace.
            $this->prefixDirsPsr4[$prefix] = array_merge(
                (array) $paths,
                $this->prefixDirsPsr4[$prefix]
            );
        } else {
            // Append directories for an already registered namespace.
            $this->prefixDirsPsr4[$prefix] = array_merge(
                $this->prefixDirsPsr4[$prefix],
                (array) $paths
            );
        }
    }

    /**
     * Registers a set of PSR-0 directories for a given prefix,
     * replacing any others previously set for this prefix.
     *
     * @param string       $prefix The prefix
     * @param array|string $paths  The PSR-0 base directories
     */
    public function set($prefix, $paths)
    {
        if (!$prefix) {
            $this->fallbackDirsPsr0 = (array) $paths;
        } else {
            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
        }
    }

    /**
     * Registers a set of PSR-4 directories for a given namespace,
     * replacing any others previously set for this namespace.
     *
     * @param string       $prefix The prefix/namespace, with trailing '\\'
     * @param array|string $paths  The PSR-4 base directories
     *
     * @throws \InvalidArgumentException
     */
    public function setPsr4($prefix, $paths)
    {
        if (!$prefix) {
            $this->fallbackDirsPsr4 = (array) $paths;
        } else {
            $length = strlen($prefix);
            if ('\\' !== $prefix[$length - 1]) {
                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
            }
            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
            $this->prefixDirsPsr4[$prefix] = (array) $paths;
        }
    }

    /**
     * Turns on searching the include path for class files.
     *
     * @param bool $useIncludePath
     */
    public function setUseIncludePath($useIncludePath)
    {
        $this->useIncludePath = $useIncludePath;
    }

    /**
     * Can be used to check if the autoloader uses the include path to check
     * for classes.
     *
     * @return bool
     */
    public function getUseIncludePath()
    {
        return $this->useIncludePath;
    }

    /**
     * Turns off searching the prefix and fallback directories for classes
     * that have not been registered with the class map.
     *
     * @param bool $classMapAuthoritative
     */
    public function setClassMapAuthoritative($classMapAuthoritative)
    {
        $this->classMapAuthoritative = $classMapAuthoritative;
    }

    /**
     * Should class lookup fail if not found in the current class map?
     *
     * @return bool
     */
    public function isClassMapAuthoritative()
    {
        return $this->classMapAuthoritative;
    }

    /**
     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
     *
     * @param string|null $apcuPrefix
     */
    public function setApcuPrefix($apcuPrefix)
    {
        $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
    }

    /**
     * The APCu prefix in use, or null if APCu caching is not enabled.
     *
     * @return string|null
     */
    public function getApcuPrefix()
    {
        return $this->apcuPrefix;
    }

    /**
     * Registers this instance as an autoloader.
     *
     * @param bool $prepend Whether to prepend the autoloader or not
     */
    public function register($prepend = false)
    {
        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
    }

    /**
     * Unregisters this instance as an autoloader.
     */
    public function unregister()
    {
        spl_autoload_unregister(array($this, 'loadClass'));
    }

    /**
     * Loads the given class or interface.
     *
     * @param  string    $class The name of the class
     * @return bool|null True if loaded, null otherwise
     */
    public function loadClass($class)
    {
        if ($file = $this->findFile($class)) {
            includeFile($file);

            return true;
        }
    }

    /**
     * Finds the path to the file where the class is defined.
     *
     * @param string $class The name of the class
     *
     * @return string|false The path if found, false otherwise
     */
    public function findFile($class)
    {
        // class map lookup
        if (isset($this->classMap[$class])) {
            return $this->classMap[$class];
        }
        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
            return false;
        }
        if (null !== $this->apcuPrefix) {
            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
            if ($hit) {
                return $file;
            }
        }

        $file = $this->findFileWithExtension($class, '.php');

        // Search for Hack files if we are running on HHVM
        if (false === $file && defined('HHVM_VERSION')) {
            $file = $this->findFileWithExtension($class, '.hh');
        }

        if (null !== $this->apcuPrefix) {
            apcu_add($this->apcuPrefix.$class, $file);
        }

        if (false === $file) {
            // Remember that this class does not exist.
            $this->missingClasses[$class] = true;
        }

        return $file;
    }

    private function findFileWithExtension($class, $ext)
    {
        // PSR-4 lookup
        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;

        $first = $class[0];
        if (isset($this->prefixLengthsPsr4[$first])) {
            $subPath = $class;
            while (false !== $lastPos = strrpos($subPath, '\\')) {
                $subPath = substr($subPath, 0, $lastPos);
                $search = $subPath.'\\';
                if (isset($this->prefixDirsPsr4[$search])) {
                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
                        $length = $this->prefixLengthsPsr4[$first][$search];
                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
                            return $file;
                        }
                    }
                }
            }
        }

        // PSR-4 fallback dirs
        foreach ($this->fallbackDirsPsr4 as $dir) {
            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
                return $file;
            }
        }

        // PSR-0 lookup
        if (false !== $pos = strrpos($class, '\\')) {
            // namespaced class name
            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
        } else {
            // PEAR-like class name
            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
        }

        if (isset($this->prefixesPsr0[$first])) {
            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
                if (0 === strpos($class, $prefix)) {
                    foreach ($dirs as $dir) {
                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                            return $file;
                        }
                    }
                }
            }
        }

        // PSR-0 fallback dirs
        foreach ($this->fallbackDirsPsr0 as $dir) {
            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                return $file;
            }
        }

        // PSR-0 include paths.
        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
            return $file;
        }

        return false;
    }
}

/**
 * Scope isolated include.
 *
 * Prevents access to $this/self from included files.
 */
function includeFile($file)
{
    include $file;
}
<?php

// include_paths.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    $vendorDir . '/phing/phing/classes',
);
<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the MIT license. For more information, see
 * <http://www.doctrine-project.org>.
 */

namespace Doctrine\Instantiator\Exception;

/**
 * Base exception marker interface for the instantiator component
 *
 * @author Marco Pivetta <ocramius@gmail.com>
 */
interface ExceptionInterface
{
}
<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the MIT license. For more information, see
 * <http://www.doctrine-project.org>.
 */

namespace Doctrine\Instantiator\Exception;

use InvalidArgumentException as BaseInvalidArgumentException;
use ReflectionClass;

/**
 * Exception for invalid arguments provided to the instantiator
 *
 * @author Marco Pivetta <ocramius@gmail.com>
 */
class InvalidArgumentException extends BaseInvalidArgumentException implements ExceptionInterface
{
    /**
     * @param string $className
     *
     * @return self
     */
    public static function fromNonExistingClass($className)
    {
        if (interface_exists($className)) {
            return new self(sprintf('The provided type "%s" is an interface, and can not be instantiated', $className));
        }

        if (PHP_VERSION_ID >= 50400 && trait_exists($className)) {
            return new self(sprintf('The provided type "%s" is a trait, and can not be instantiated', $className));
        }

        return new self(sprintf('The provided class "%s" does not exist', $className));
    }

    /**
     * @param ReflectionClass $reflectionClass
     *
     * @return self
     */
    public static function fromAbstractClass(ReflectionClass $reflectionClass)
    {
        return new self(sprintf(
            'The provided class "%s" is abstract, and can not be instantiated',
            $reflectionClass->getName()
        ));
    }
}
<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the MIT license. For more information, see
 * <http://www.doctrine-project.org>.
 */

namespace Doctrine\Instantiator\Exception;

use Exception;
use ReflectionClass;
use UnexpectedValueException as BaseUnexpectedValueException;

/**
 * Exception for given parameters causing invalid/unexpected state on instantiation
 *
 * @author Marco Pivetta <ocramius@gmail.com>
 */
class UnexpectedValueException extends BaseUnexpectedValueException implements ExceptionInterface
{
    /**
     * @param ReflectionClass $reflectionClass
     * @param Exception       $exception
     *
     * @return self
     */
    public static function fromSerializationTriggeredException(ReflectionClass $reflectionClass, Exception $exception)
    {
        return new self(
            sprintf(
                'An exception was raised while trying to instantiate an instance of "%s" via un-serialization',
                $reflectionClass->getName()
            ),
            0,
            $exception
        );
    }

    /**
     * @param ReflectionClass $reflectionClass
     * @param string          $errorString
     * @param int             $errorCode
     * @param string          $errorFile
     * @param int             $errorLine
     *
     * @return UnexpectedValueException
     */
    public static function fromUncleanUnSerialization(
        ReflectionClass $reflectionClass,
        $errorString,
        $errorCode,
        $errorFile,
        $errorLine
    ) {
        return new self(
            sprintf(
                'Could not produce an instance of "%s" via un-serialization, since an error was triggered '
                . 'in file "%s" at line "%d"',
                $reflectionClass->getName(),
                $errorFile,
                $errorLine
            ),
            0,
            new Exception($errorString, $errorCode)
        );
    }
}
<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the MIT license. For more information, see
 * <http://www.doctrine-project.org>.
 */

namespace Doctrine\Instantiator;

use Closure;
use Doctrine\Instantiator\Exception\InvalidArgumentException;
use Doctrine\Instantiator\Exception\UnexpectedValueException;
use Exception;
use ReflectionClass;

/**
 * {@inheritDoc}
 *
 * @author Marco Pivetta <ocramius@gmail.com>
 */
final class Instantiator implements InstantiatorInterface
{
    /**
     * Markers used internally by PHP to define whether {@see \unserialize} should invoke
     * the method {@see \Serializable::unserialize()} when dealing with classes implementing
     * the {@see \Serializable} interface.
     */
    const SERIALIZATION_FORMAT_USE_UNSERIALIZER   = 'C';
    const SERIALIZATION_FORMAT_AVOID_UNSERIALIZER = 'O';

    /**
     * @var \Closure[] of {@see \Closure} instances used to instantiate specific classes
     */
    private static $cachedInstantiators = array();

    /**
     * @var object[] of objects that can directly be cloned
     */
    private static $cachedCloneables = array();

    /**
     * {@inheritDoc}
     */
    public function instantiate($className)
    {
        if (isset(self::$cachedCloneables[$className])) {
            return clone self::$cachedCloneables[$className];
        }

        if (isset(self::$cachedInstantiators[$className])) {
            $factory = self::$cachedInstantiators[$className];

            return $factory();
        }

        return $this->buildAndCacheFromFactory($className);
    }

    /**
     * Builds the requested object and caches it in static properties for performance
     *
     * @param string $className
     *
     * @return object
     */
    private function buildAndCacheFromFactory($className)
    {
        $factory  = self::$cachedInstantiators[$className] = $this->buildFactory($className);
        $instance = $factory();

        if ($this->isSafeToClone(new ReflectionClass($instance))) {
            self::$cachedCloneables[$className] = clone $instance;
        }

        return $instance;
    }

    /**
     * Builds a {@see \Closure} capable of instantiating the given $className without
     * invoking its constructor.
     *
     * @param string $className
     *
     * @return Closure
     */
    private function buildFactory($className)
    {
        $reflectionClass = $this->getReflectionClass($className);

        if ($this->isInstantiableViaReflection($reflectionClass)) {
            return function () use ($reflectionClass) {
                return $reflectionClass->newInstanceWithoutConstructor();
            };
        }

        $serializedString = sprintf(
            '%s:%d:"%s":0:{}',
            $this->getSerializationFormat($reflectionClass),
            strlen($className),
            $className
        );

        $this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString);

        return function () use ($serializedString) {
            return unserialize($serializedString);
        };
    }

    /**
     * @param string $className
     *
     * @return ReflectionClass
     *
     * @throws InvalidArgumentException
     */
    private function getReflectionClass($className)
    {
        if (! class_exists($className)) {
            throw InvalidArgumentException::fromNonExistingClass($className);
        }

        $reflection = new ReflectionClass($className);

        if ($reflection->isAbstract()) {
            throw InvalidArgumentException::fromAbstractClass($reflection);
        }

        return $reflection;
    }

    /**
     * @param ReflectionClass $reflectionClass
     * @param string          $serializedString
     *
     * @throws UnexpectedValueException
     *
     * @return void
     */
    private function checkIfUnSerializationIsSupported(ReflectionClass $reflectionClass, $serializedString)
    {
        set_error_handler(function ($code, $message, $file, $line) use ($reflectionClass, & $error) {
            $error = UnexpectedValueException::fromUncleanUnSerialization(
                $reflectionClass,
                $message,
                $code,
                $file,
                $line
            );
        });

        $this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString);

        restore_error_handler();

        if ($error) {
            throw $error;
        }
    }

    /**
     * @param ReflectionClass $reflectionClass
     * @param string          $serializedString
     *
     * @throws UnexpectedValueException
     *
     * @return void
     */
    private function attemptInstantiationViaUnSerialization(ReflectionClass $reflectionClass, $serializedString)
    {
        try {
            unserialize($serializedString);
        } catch (Exception $exception) {
            restore_error_handler();

            throw UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $exception);
        }
    }

    /**
     * @param ReflectionClass $reflectionClass
     *
     * @return bool
     */
    private function isInstantiableViaReflection(ReflectionClass $reflectionClass)
    {
        if (\PHP_VERSION_ID >= 50600) {
            return ! ($this->hasInternalAncestors($reflectionClass) && $reflectionClass->isFinal());
        }

        return \PHP_VERSION_ID >= 50400 && ! $this->hasInternalAncestors($reflectionClass);
    }

    /**
     * Verifies whether the given class is to be considered internal
     *
     * @param ReflectionClass $reflectionClass
     *
     * @return bool
     */
    private function hasInternalAncestors(ReflectionClass $reflectionClass)
    {
        do {
            if ($reflectionClass->isInternal()) {
                return true;
            }
        } while ($reflectionClass = $reflectionClass->getParentClass());

        return false;
    }

    /**
     * Verifies if the given PHP version implements the `Serializable` interface serialization
     * with an incompatible serialization format. If that's the case, use serialization marker
     * "C" instead of "O".
     *
     * @link http://news.php.net/php.internals/74654
     *
     * @param ReflectionClass $reflectionClass
     *
     * @return string the serialization format marker, either self::SERIALIZATION_FORMAT_USE_UNSERIALIZER
     *                or self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER
     */
    private function getSerializationFormat(ReflectionClass $reflectionClass)
    {
        if ($this->isPhpVersionWithBrokenSerializationFormat()
            && $reflectionClass->implementsInterface('Serializable')
        ) {
            return self::SERIALIZATION_FORMAT_USE_UNSERIALIZER;
        }

        return self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER;
    }

    /**
     * Checks whether the current PHP runtime uses an incompatible serialization format
     *
     * @return bool
     */
    private function isPhpVersionWithBrokenSerializationFormat()
    {
        return PHP_VERSION_ID === 50429 || PHP_VERSION_ID === 50513;
    }

    /**
     * Checks if a class is cloneable
     *
     * @param ReflectionClass $reflection
     *
     * @return bool
     */
    private function isSafeToClone(ReflectionClass $reflection)
    {
        if (method_exists($reflection, 'isCloneable') && ! $reflection->isCloneable()) {
            return false;
        }

        // not cloneable if it implements `__clone`, as we want to avoid calling it
        return ! $reflection->hasMethod('__clone');
    }
}
<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the MIT license. For more information, see
 * <http://www.doctrine-project.org>.
 */

namespace Doctrine\Instantiator;

/**
 * Instantiator provides utility methods to build objects without invoking their constructors
 *
 * @author Marco Pivetta <ocramius@gmail.com>
 */
interface InstantiatorInterface
{
    /**
     * @param string $className
     *
     * @return object
     *
     * @throws \Doctrine\Instantiator\Exception\ExceptionInterface
     */
    public function instantiate($className);
}
<?php
namespace Firebase\JWT;

class BeforeValidException extends \UnexpectedValueException
{

}
<?php
namespace Firebase\JWT;

class ExpiredException extends \UnexpectedValueException
{

}
<?php

namespace Firebase\JWT;
use \DomainException;
use \InvalidArgumentException;
use \UnexpectedValueException;
use \DateTime;

/**
 * JSON Web Token implementation, based on this spec:
 * https://tools.ietf.org/html/rfc7519
 *
 * PHP version 5
 *
 * @category Authentication
 * @package  Authentication_JWT
 * @author   Neuman Vong <neuman@twilio.com>
 * @author   Anant Narayanan <anant@php.net>
 * @license  http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
 * @link     https://github.com/firebase/php-jwt
 */
class JWT
{

    /**
     * When checking nbf, iat or expiration times,
     * we want to provide some extra leeway time to
     * account for clock skew.
     */
    public static $leeway = 0;

    /**
     * Allow the current timestamp to be specified.
     * Useful for fixing a value within unit testing.
     *
     * Will default to PHP time() value if null.
     */
    public static $timestamp = null;

    public static $supported_algs = array(
        'HS256' => array('hash_hmac', 'SHA256'),
        'HS512' => array('hash_hmac', 'SHA512'),
        'HS384' => array('hash_hmac', 'SHA384'),
        'RS256' => array('openssl', 'SHA256'),
        'RS384' => array('openssl', 'SHA384'),
        'RS512' => array('openssl', 'SHA512'),
    );

    /**
     * Decodes a JWT string into a PHP object.
     *
     * @param string        $jwt            The JWT
     * @param string|array  $key            The key, or map of keys.
     *                                      If the algorithm used is asymmetric, this is the public key
     * @param array         $allowed_algs   List of supported verification algorithms
     *                                      Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
     *
     * @return object The JWT's payload as a PHP object
     *
     * @throws UnexpectedValueException     Provided JWT was invalid
     * @throws SignatureInvalidException    Provided JWT was invalid because the signature verification failed
     * @throws BeforeValidException         Provided JWT is trying to be used before it's eligible as defined by 'nbf'
     * @throws BeforeValidException         Provided JWT is trying to be used before it's been created as defined by 'iat'
     * @throws ExpiredException             Provided JWT has since expired, as defined by the 'exp' claim
     *
     * @uses jsonDecode
     * @uses urlsafeB64Decode
     */
    public static function decode($jwt, $key, array $allowed_algs = array())
    {
        $timestamp = is_null(static::$timestamp) ? time() : static::$timestamp;

        if (empty($key)) {
            throw new InvalidArgumentException('Key may not be empty');
        }
        $tks = explode('.', $jwt);
        if (count($tks) != 3) {
            throw new UnexpectedValueException('Wrong number of segments');
        }
        list($headb64, $bodyb64, $cryptob64) = $tks;
        if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
            throw new UnexpectedValueException('Invalid header encoding');
        }
        if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
            throw new UnexpectedValueException('Invalid claims encoding');
        }
        if (false === ($sig = static::urlsafeB64Decode($cryptob64))) {
            throw new UnexpectedValueException('Invalid signature encoding');
        }
        if (empty($header->alg)) {
            throw new UnexpectedValueException('Empty algorithm');
        }
        if (empty(static::$supported_algs[$header->alg])) {
            throw new UnexpectedValueException('Algorithm not supported');
        }
        if (!in_array($header->alg, $allowed_algs)) {
            throw new UnexpectedValueException('Algorithm not allowed');
        }
        if (is_array($key) || $key instanceof \ArrayAccess) {
            if (isset($header->kid)) {
                if (!isset($key[$header->kid])) {
                    throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
                }
                $key = $key[$header->kid];
            } else {
                throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
            }
        }

        // Check the signature
        if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) {
            throw new SignatureInvalidException('Signature verification failed');
        }

        // Check if the nbf if it is defined. This is the time that the
        // token can actually be used. If it's not yet that time, abort.
        if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
            throw new BeforeValidException(
                'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->nbf)
            );
        }

        // Check that this token has been created before 'now'. This prevents
        // using tokens that have been created for later use (and haven't
        // correctly used the nbf claim).
        if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
            throw new BeforeValidException(
                'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->iat)
            );
        }

        // Check if this token has expired.
        if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
            throw new ExpiredException('Expired token');
        }

        return $payload;
    }

    /**
     * Converts and signs a PHP object or array into a JWT string.
     *
     * @param object|array  $payload    PHP object or array
     * @param string        $key        The secret key.
     *                                  If the algorithm used is asymmetric, this is the private key
     * @param string        $alg        The signing algorithm.
     *                                  Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
     * @param mixed         $keyId
     * @param array         $head       An array with header elements to attach
     *
     * @return string A signed JWT
     *
     * @uses jsonEncode
     * @uses urlsafeB64Encode
     */
    public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
    {
        $header = array('typ' => 'JWT', 'alg' => $alg);
        if ($keyId !== null) {
            $header['kid'] = $keyId;
        }
        if ( isset($head) && is_array($head) ) {
            $header = array_merge($head, $header);
        }
        $segments = array();
        $segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
        $segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
        $signing_input = implode('.', $segments);

        $signature = static::sign($signing_input, $key, $alg);
        $segments[] = static::urlsafeB64Encode($signature);

        return implode('.', $segments);
    }

    /**
     * Sign a string with a given key and algorithm.
     *
     * @param string            $msg    The message to sign
     * @param string|resource   $key    The secret key
     * @param string            $alg    The signing algorithm.
     *                                  Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
     *
     * @return string An encrypted message
     *
     * @throws DomainException Unsupported algorithm was specified
     */
    public static function sign($msg, $key, $alg = 'HS256')
    {
        if (empty(static::$supported_algs[$alg])) {
            throw new DomainException('Algorithm not supported');
        }
        list($function, $algorithm) = static::$supported_algs[$alg];
        switch($function) {
            case 'hash_hmac':
                return hash_hmac($algorithm, $msg, $key, true);
            case 'openssl':
                $signature = '';
                $success = openssl_sign($msg, $signature, $key, $algorithm);
                if (!$success) {
                    throw new DomainException("OpenSSL unable to sign data");
                } else {
                    return $signature;
                }
        }
    }

    /**
     * Verify a signature with the message, key and method. Not all methods
     * are symmetric, so we must have a separate verify and sign method.
     *
     * @param string            $msg        The original message (header and body)
     * @param string            $signature  The original signature
     * @param string|resource   $key        For HS*, a string key works. for RS*, must be a resource of an openssl public key
     * @param string            $alg        The algorithm
     *
     * @return bool
     *
     * @throws DomainException Invalid Algorithm or OpenSSL failure
     */
    private static function verify($msg, $signature, $key, $alg)
    {
        if (empty(static::$supported_algs[$alg])) {
            throw new DomainException('Algorithm not supported');
        }

        list($function, $algorithm) = static::$supported_algs[$alg];
        switch($function) {
            case 'openssl':
                $success = openssl_verify($msg, $signature, $key, $algorithm);
                if ($success === 1) {
                    return true;
                } elseif ($success === 0) {
                    return false;
                }
                // returns 1 on success, 0 on failure, -1 on error.
                throw new DomainException(
                    'OpenSSL error: ' . openssl_error_string()
                );
            case 'hash_hmac':
            default:
                $hash = hash_hmac($algorithm, $msg, $key, true);
                if (function_exists('hash_equals')) {
                    return hash_equals($signature, $hash);
                }
                $len = min(static::safeStrlen($signature), static::safeStrlen($hash));

                $status = 0;
                for ($i = 0; $i < $len; $i++) {
                    $status |= (ord($signature[$i]) ^ ord($hash[$i]));
                }
                $status |= (static::safeStrlen($signature) ^ static::safeStrlen($hash));

                return ($status === 0);
        }
    }

    /**
     * Decode a JSON string into a PHP object.
     *
     * @param string $input JSON string
     *
     * @return object Object representation of JSON string
     *
     * @throws DomainException Provided string was invalid JSON
     */
    public static function jsonDecode($input)
    {
        if (version_compare(PHP_VERSION, '5.4.0', '>=') && !(defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
            /** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you
             * to specify that large ints (like Steam Transaction IDs) should be treated as
             * strings, rather than the PHP default behaviour of converting them to floats.
             */
            $obj = json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
        } else {
            /** Not all servers will support that, however, so for older versions we must
             * manually detect large ints in the JSON string and quote them (thus converting
             *them to strings) before decoding, hence the preg_replace() call.
             */
            $max_int_length = strlen((string) PHP_INT_MAX) - 1;
            $json_without_bigints = preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input);
            $obj = json_decode($json_without_bigints);
        }

        if (function_exists('json_last_error') && $errno = json_last_error()) {
            static::handleJsonError($errno);
        } elseif ($obj === null && $input !== 'null') {
            throw new DomainException('Null result with non-null input');
        }
        return $obj;
    }

    /**
     * Encode a PHP object into a JSON string.
     *
     * @param object|array $input A PHP object or array
     *
     * @return string JSON representation of the PHP object or array
     *
     * @throws DomainException Provided object could not be encoded to valid JSON
     */
    public static function jsonEncode($input)
    {
        $json = json_encode($input);
        if (function_exists('json_last_error') && $errno = json_last_error()) {
            static::handleJsonError($errno);
        } elseif ($json === 'null' && $input !== null) {
            throw new DomainException('Null result with non-null input');
        }
        return $json;
    }

    /**
     * Decode a string with URL-safe Base64.
     *
     * @param string $input A Base64 encoded string
     *
     * @return string A decoded string
     */
    public static function urlsafeB64Decode($input)
    {
        $remainder = strlen($input) % 4;
        if ($remainder) {
            $padlen = 4 - $remainder;
            $input .= str_repeat('=', $padlen);
        }
        return base64_decode(strtr($input, '-_', '+/'));
    }

    /**
     * Encode a string with URL-safe Base64.
     *
     * @param string $input The string you want encoded
     *
     * @return string The base64 encode of what you passed in
     */
    public static function urlsafeB64Encode($input)
    {
        return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
    }

    /**
     * Helper method to create a JSON error.
     *
     * @param int $errno An error number from json_last_error()
     *
     * @return void
     */
    private static function handleJsonError($errno)
    {
        $messages = array(
            JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
            JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
            JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
            JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
            JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
        );
        throw new DomainException(
            isset($messages[$errno])
            ? $messages[$errno]
            : 'Unknown JSON error: ' . $errno
        );
    }

    /**
     * Get the number of bytes in cryptographic strings.
     *
     * @param string
     *
     * @return int
     */
    private static function safeStrlen($str)
    {
        if (function_exists('mb_strlen')) {
            return mb_strlen($str, '8bit');
        }
        return strlen($str);
    }
}
<?php
namespace Firebase\JWT;

class SignatureInvalidException extends \UnexpectedValueException
{

}
<?php

Phar::mapPhar('guzzle.phar');

require_once 'phar://guzzle.phar/vendor/symfony/class-loader/Symfony/Component/ClassLoader/UniversalClassLoader.php';

$classLoader = new Symfony\Component\ClassLoader\UniversalClassLoader();
$classLoader->registerNamespaces(array(
    'Guzzle' => 'phar://guzzle.phar/src',
    'Symfony\\Component\\EventDispatcher' => 'phar://guzzle.phar/vendor/symfony/event-dispatcher',
    'Doctrine' => 'phar://guzzle.phar/vendor/doctrine/common/lib',
    'Monolog' => 'phar://guzzle.phar/vendor/monolog/monolog/src'
));
$classLoader->register();

__HALT_COMPILER();
<?php
/**
 * Phing task for composer validation.
 *
 * @copyright 2012 Clay Loveless <clay@php.net>
 * @license   http://claylo.mit-license.org/2012/ MIT License
 */

require_once 'phing/Task.php';

class ComposerLintTask extends Task
{
    protected $dir = null;
    protected $file = null;
    protected $passthru = false;
    protected $composer = null;

    /**
     * The setter for the dir
     *
     * @param string $str Directory to crawl recursively for composer files
     */
    public function setDir($str)
    {
        $this->dir = $str;
    }

    /**
     * The setter for the file
     *
     * @param string $str Individual file to validate
     */
    public function setFile($str)
    {
        $this->file = $str;
    }

    /**
     * Whether to use PHP's passthru() function instead of exec()
     *
     * @param boolean $passthru If passthru shall be used
     */
    public function setPassthru($passthru)
    {
        $this->passthru = (bool) $passthru;
    }

    /**
     * Composer to execute. If unset, will attempt composer.phar in project
     * basedir, and if that fails, will attempt global composer
     * installation.
     *
     * @param string $str Individual file to validate
     */
    public function setComposer($str)
    {
        $this->file = $str;
    }

    /**
     * The init method: do init steps
     */
    public function init()
    {
        // nothing needed here
    }

    /**
     * The main entry point
     */
    public function main()
    {
        if ($this->composer === null) {
            $this->findComposer();
        }

        $files = array();
        if (!empty($this->file) && file_exists($this->file)) {
            $files[] = $this->file;
        }

        if (!empty($this->dir)) {
            $found = $this->findFiles();
            foreach ($found as $file) {
                $files[] = $this->dir . DIRECTORY_SEPARATOR . $file;
            }
        }

        foreach ($files as $file) {

            $cmd = $this->composer . ' validate ' . $file;
            $cmd = escapeshellcmd($cmd);

            if ($this->passthru) {
                $retval = null;
                passthru($cmd, $retval);
                if ($retval == 1) {
                    throw new BuildException('invalid composer.json');
                }
            } else {
                $out = array();
                $retval = null;
                exec($cmd, $out, $retval);
                if ($retval == 1) {
                    $err = join("\n", $out);
                    throw new BuildException($err);
                } else {
                    $this->log($out[0]);
                }
            }

        }

    }

    /**
     * Find the composer.json files using Phing's directory scanner
     *
     * @return array
     */
    protected function findFiles()
    {
        $ds = new DirectoryScanner();
        $ds->setBasedir($this->dir);
        $ds->setIncludes(array('**/composer.json'));
        $ds->scan();
        return $ds->getIncludedFiles();
    }

    /**
     * Find composer installation
     *
     */
    protected function findComposer()
    {
        $basedir = $this->project->getBasedir();
        $php = $this->project->getProperty('php.interpreter');

        if (file_exists($basedir . '/composer.phar')) {
            $this->composer = "$php $basedir/composer.phar";
        } else {
            $out = array();
            exec('which composer', $out);
            if (empty($out)) {
                throw new BuildException(
                    'Could not determine composer location.'
                );
            }
            $this->composer = $out[0];
        }
    }
}
<?php
/**
 * This file is part of Guzzle's build process.
 *
 * @copyright 2012 Clay Loveless <clay@php.net>
 * @license   http://claylo.mit-license.org/2012/ MIT License
 */

require_once 'phing/Task.php';
require_once 'PEAR/PackageFileManager2.php';
require_once 'PEAR/PackageFileManager/File.php';
require_once 'PEAR/Packager.php';

class GuzzlePearPharPackageTask extends Task
{
    private $version;
    private $deploy = true;
    private $makephar = true;

    private $subpackages = array();

    public function setVersion($str)
    {
        $this->version = $str;
    }

    public function getVersion()
    {
        return $this->version;
    }

    public function setDeploy($deploy)
    {
        $this->deploy = (bool) $deploy;
    }

    public function getDeploy()
    {
        return $this->deploy;
    }

    public function setMakephar($makephar)
    {
        $this->makephar = (bool) $makephar;
    }

    public function getMakephar()
    {
        return $this->makephar;
    }

    private $basedir;
    private $guzzleinfo;
    private $changelog_release_date;
    private $changelog_notes = '-';

    public function main()
    {
        $this->basedir = $this->getProject()->getBasedir();

        if (!is_dir((string) $this->basedir.'/.subsplit')) {
            throw new BuildException('PEAR packaging requires .subsplit directory');
        }

        // main composer file
        $composer_file = file_get_contents((string) $this->basedir.'/.subsplit/composer.json');
        $this->guzzleinfo = json_decode($composer_file, true);

        // make sure we have a target
        $pearwork = (string) $this->basedir . '/build/pearwork';
        if (!is_dir($pearwork)) {
            mkdir($pearwork, 0777, true);
        }
        $pearlogs = (string) $this->basedir . '/build/artifacts/logs';
        if (!is_dir($pearlogs)) {
            mkdir($pearlogs, 0777, true);
        }

        $version = $this->getVersion();
        $this->grabChangelog();
        if ($version[0] == '2') {
            $this->log('building single PEAR package');
            $this->buildSinglePackage();
        } else {
            // $this->log("building PEAR subpackages");
            // $this->createSubPackages();
            // $this->log("building PEAR bundle package");
            $this->buildSinglePackage();
        }

        if ($this->getMakephar()) {
            $this->log("building PHAR");
            $this->getProject()->executeTarget('package-phar');
        }

        if ($this->getDeploy()) {
            $this->doDeployment();
        }
    }

    public function doDeployment()
    {
        $basedir = (string) $this->basedir;
        $this->log('beginning PEAR/PHAR deployment');

        chdir($basedir . '/build/pearwork');
        if (!is_dir('./channel')) {
            mkdir('./channel');
        }

        // Pull the PEAR channel down locally
        passthru('aws s3 sync s3://pear.guzzlephp.org ./channel');

        // add PEAR packages
        foreach (scandir('./') as $file) {
            if (substr($file, -4) == '.tgz') {
                passthru('pirum add ./channel ' . $file);
            }
        }

        // if we have a new phar, add it
        if ($this->getMakephar() && file_exists($basedir . '/build/artifacts/guzzle.phar')) {
            rename($basedir . '/build/artifacts/guzzle.phar', './channel/guzzle.phar');
        }

        // Sync up with the S3 bucket
        chdir($basedir . '/build/pearwork/channel');
        passthru('aws s3 sync . s3://pear.guzzlephp.org');
    }

    public function buildSinglePackage()
    {
        $v = $this->getVersion();
        $apiversion = $v[0] . '.0.0';

        $opts = array(
            'packagedirectory' => (string) $this->basedir . '/.subsplit/src/',
            'filelistgenerator' => 'file',
            'ignore' => array('*composer.json'),
            'baseinstalldir' => '/',
            'packagefile' => 'package.xml'
            //'outputdirectory' => (string) $this->basedir . '/build/pearwork/'
        );
        $pfm = new PEAR_PackageFileManager2();
        $pfm->setOptions($opts);
        $pfm->addRole('md', 'doc');
        $pfm->addRole('pem', 'php');
        $pfm->setPackage('Guzzle');
        $pfm->setSummary("Object-oriented PHP HTTP Client for PHP 5.3+");
        $pfm->setDescription($this->guzzleinfo['description']);
        $pfm->setPackageType('php');
        $pfm->setChannel('guzzlephp.org/pear');
        $pfm->setAPIVersion($apiversion);
        $pfm->setReleaseVersion($this->getVersion());
        $pfm->setAPIStability('stable');
        $pfm->setReleaseStability('stable');
        $pfm->setNotes($this->changelog_notes);
        $pfm->setPackageType('php');
        $pfm->setLicense('MIT', 'http://github.com/guzzle/guzzle/blob/master/LICENSE');
        $pfm->addMaintainer('lead', 'mtdowling', 'Michael Dowling', 'mtdowling@gmail.com', 'yes');
        $pfm->setDate($this->changelog_release_date);
        $pfm->generateContents();

        $phpdep = $this->guzzleinfo['require']['php'];
        $phpdep = str_replace('>=', '', $phpdep);
        $pfm->setPhpDep($phpdep);
        $pfm->addExtensionDep('required', 'curl');
        $pfm->setPearinstallerDep('1.4.6');
        $pfm->addPackageDepWithChannel('required', 'EventDispatcher', 'pear.symfony.com', '2.1.0');
        if (!empty($this->subpackages)) {
            foreach ($this->subpackages as $package) {
                $pkg = dirname($package);
                $pkg = str_replace('/', '_', $pkg);
                $pfm->addConflictingPackageDepWithChannel($pkg, 'guzzlephp.org/pear', false, $apiversion);
            }
        }

        ob_start();
        $startdir = getcwd();
        chdir((string) $this->basedir . '/build/pearwork');

        echo "DEBUGGING GENERATED PACKAGE FILE\n";
        $result = $pfm->debugPackageFile();
        if ($result) {
            $out = $pfm->writePackageFile();
            echo "\n\n\nWRITE PACKAGE FILE RESULT:\n";
            var_dump($out);
            // load up package file and build package
            $packager = new PEAR_Packager();
            echo "\n\n\nBUILDING PACKAGE FROM PACKAGE FILE:\n";
            $dest_package = $packager->package($opts['packagedirectory'].'package.xml');
            var_dump($dest_package);
        } else {
            echo "\n\n\nDEBUGGING RESULT:\n";
            var_dump($result);
        }
        echo "removing package.xml";
        unlink($opts['packagedirectory'].'package.xml');
        $log = ob_get_clean();
        file_put_contents((string) $this->basedir . '/build/artifacts/logs/pear_package.log', $log);
        chdir($startdir);
    }

    public function createSubPackages()
    {
        $this->findComponents();

        foreach ($this->subpackages as $package) {
            $baseinstalldir = dirname($package);
            $dir = (string) $this->basedir.'/.subsplit/src/' . $baseinstalldir;
            $composer_file = file_get_contents((string) $this->basedir.'/.subsplit/src/'. $package);
            $package_info = json_decode($composer_file, true);
            $this->log('building ' . $package_info['target-dir'] . ' subpackage');
            $this->buildSubPackage($dir, $baseinstalldir, $package_info);
        }
    }

    public function buildSubPackage($dir, $baseinstalldir, $info)
    {
        $package = str_replace('/', '_', $baseinstalldir);
        $opts = array(
            'packagedirectory' => $dir,
            'filelistgenerator' => 'file',
            'ignore' => array('*composer.json', '*package.xml'),
            'baseinstalldir' => '/' . $info['target-dir'],
            'packagefile' => 'package.xml'
        );
        $pfm = new PEAR_PackageFileManager2();
        $pfm->setOptions($opts);
        $pfm->setPackage($package);
        $pfm->setSummary($info['description']);
        $pfm->setDescription($info['description']);
        $pfm->setPackageType('php');
        $pfm->setChannel('guzzlephp.org/pear');
        $pfm->setAPIVersion('3.0.0');
        $pfm->setReleaseVersion($this->getVersion());
        $pfm->setAPIStability('stable');
        $pfm->setReleaseStability('stable');
        $pfm->setNotes($this->changelog_notes);
        $pfm->setPackageType('php');
        $pfm->setLicense('MIT', 'http://github.com/guzzle/guzzle/blob/master/LICENSE');
        $pfm->addMaintainer('lead', 'mtdowling', 'Michael Dowling', 'mtdowling@gmail.com', 'yes');
        $pfm->setDate($this->changelog_release_date);
        $pfm->generateContents();

        $phpdep = $this->guzzleinfo['require']['php'];
        $phpdep = str_replace('>=', '', $phpdep);
        $pfm->setPhpDep($phpdep);
        $pfm->setPearinstallerDep('1.4.6');

        foreach ($info['require'] as $type => $version) {
            if ($type == 'php') {
                continue;
            }
            if ($type == 'symfony/event-dispatcher') {
                $pfm->addPackageDepWithChannel('required', 'EventDispatcher', 'pear.symfony.com', '2.1.0');
            }
            if ($type == 'ext-curl') {
                $pfm->addExtensionDep('required', 'curl');
            }
            if (substr($type, 0, 6) == 'guzzle') {
                $gdep = str_replace('/', ' ', $type);
                $gdep = ucwords($gdep);
                $gdep = str_replace(' ', '_', $gdep);
                $pfm->addPackageDepWithChannel('required', $gdep, 'guzzlephp.org/pear', $this->getVersion());
            }
        }

        // can't have main Guzzle package AND sub-packages
        $pfm->addConflictingPackageDepWithChannel('Guzzle', 'guzzlephp.org/pear', false, $apiversion);

        ob_start();
        $startdir = getcwd();
        chdir((string) $this->basedir . '/build/pearwork');

        echo "DEBUGGING GENERATED PACKAGE FILE\n";
        $result = $pfm->debugPackageFile();
        if ($result) {
            $out = $pfm->writePackageFile();
            echo "\n\n\nWRITE PACKAGE FILE RESULT:\n";
            var_dump($out);
            // load up package file and build package
            $packager = new PEAR_Packager();
            echo "\n\n\nBUILDING PACKAGE FROM PACKAGE FILE:\n";
            $dest_package = $packager->package($opts['packagedirectory'].'/package.xml');
            var_dump($dest_package);
        } else {
            echo "\n\n\nDEBUGGING RESULT:\n";
            var_dump($result);
        }
        echo "removing package.xml";
        unlink($opts['packagedirectory'].'/package.xml');
        $log = ob_get_clean();
        file_put_contents((string) $this->basedir . '/build/artifacts/logs/pear_package_'.$package.'.log', $log);
        chdir($startdir);
    }

    public function findComponents()
    {
        $ds = new DirectoryScanner();
        $ds->setBasedir((string) $this->basedir.'/.subsplit/src');
        $ds->setIncludes(array('**/composer.json'));
        $ds->scan();
        $files = $ds->getIncludedFiles();
        $this->subpackages = $files;
    }

    public function grabChangelog()
    {
        $cl = file((string) $this->basedir.'/.subsplit/CHANGELOG.md');
        $notes = '';
        $in_version = false;
        $release_date = null;

        foreach ($cl as $line) {
            $line = trim($line);
            if (preg_match('/^\* '.$this->getVersion().' \(([0-9\-]+)\)$/', $line, $matches)) {
                $release_date = $matches[1];
                $in_version = true;
                continue;
            }
            if ($in_version && empty($line) && empty($notes)) {
                continue;
            }
            if ($in_version && ! empty($line)) {
                $notes .= $line."\n";
            }
            if ($in_version && empty($line) && !empty($notes)) {
                $in_version = false;
            }
        }
        $this->changelog_release_date = $release_date;

        if (! empty($notes)) {
            $this->changelog_notes = $notes;
        }
    }
}
<?php
/**
 * Phing wrapper around git subsplit.
 *
 * @see https://github.com/dflydev/git-subsplit
 * @copyright 2012 Clay Loveless <clay@php.net>
 * @license   http://claylo.mit-license.org/2012/ MIT License
 */

require_once 'phing/tasks/ext/git/GitBaseTask.php';

// base - base of tree to split out
// subIndicatorFile - composer.json, package.xml?
class GuzzleSubSplitTask extends GitBaseTask
{
    /**
     * What git repository to pull from and publish to
     */
    protected $remote = null;

    /**
     * Publish for comma-separated heads instead of all heads
     */
    protected $heads = null;

    /**
     * Publish for comma-separated tags instead of all tags
     */
    protected $tags = null;

    /**
     * Base of the tree RELATIVE TO .subsplit working dir
     */
    protected $base = null;

    /**
     * The presence of this file will indicate that the directory it resides
     * in is at the top level of a split.
     */
    protected $subIndicatorFile = 'composer.json';

    /**
     * Do everything except actually send the update.
     */
    protected $dryRun = null;

    /**
     * Do not sync any heads.
     */
    protected $noHeads = false;

    /**
     * Do not sync any tags.
     */
    protected $noTags = false;

    /**
     * The splits we found in the heads
     */
    protected $splits;

    public function setRemote($str)
    {
        $this->remote = $str;
    }

    public function getRemote()
    {
        return $this->remote;
    }

    public function setHeads($str)
    {
        $this->heads = explode(',', $str);
    }

    public function getHeads()
    {
        return $this->heads;
    }

    public function setTags($str)
    {
        $this->tags = explode(',', $str);
    }

    public function getTags()
    {
        return $this->tags;
    }

    public function setBase($str)
    {
        $this->base = $str;
    }

    public function getBase()
    {
        return $this->base;
    }

    public function setSubIndicatorFile($str)
    {
        $this->subIndicatorFile = $str;
    }

    public function getSubIndicatorFile()
    {
        return $this->subIndicatorFile;
    }

    public function setDryRun($bool)
    {
        $this->dryRun = (bool) $bool;
    }

    public function getDryRun()
    {
        return $this->dryRun;
    }

    public function setNoHeads($bool)
    {
        $this->noHeads = (bool) $bool;
    }

    public function getNoHeads()
    {
        return $this->noHeads;
    }

    public function setNoTags($bool)
    {
        $this->noTags = (bool) $bool;
    }

    public function getNoTags()
    {
        return $this->noTags;
    }

    /**
     * GitClient from VersionControl_Git
     */
    protected $client   = null;

    /**
     * The main entry point
     */
    public function main()
    {
        $repo = $this->getRepository();
        if (empty($repo)) {
            throw new BuildException('"repository" is a required parameter');
        }

        $remote = $this->getRemote();
        if (empty($remote)) {
            throw new BuildException('"remote" is a required parameter');
        }

        chdir($repo);
        $this->client = $this->getGitClient(false, $repo);

        // initalized yet?
        if (!is_dir('.subsplit')) {
            $this->subsplitInit();
        } else {
            // update
            $this->subsplitUpdate();
        }

        // find all splits based on heads requested
        $this->findSplits();

        // check that GitHub has the repos
        $this->verifyRepos();

        // execute the subsplits
        $this->publish();
    }

    public function publish()
    {
        $this->log('DRY RUN ONLY FOR NOW');
        $base = $this->getBase();
        $base = rtrim($base, '/') . '/';
        $org = $this->getOwningTarget()->getProject()->getProperty('github.org');

        $splits = array();

        $heads = $this->getHeads();
        foreach ($heads as $head) {
            foreach ($this->splits[$head] as $component => $meta) {
                $splits[] = $base . $component . ':git@github.com:'. $org.'/'.$meta['repo'];
            }

            $cmd = 'git subsplit publish ';
            $cmd .= escapeshellarg(implode(' ', $splits));

            if ($this->getNoHeads()) {
                $cmd .= ' --no-heads';
            } else {
                $cmd .= ' --heads='.$head;
            }

            if ($this->getNoTags()) {
                $cmd .= ' --no-tags';
            } else {
                if ($this->getTags()) {
                    $cmd .= ' --tags=' . escapeshellarg(implode(' ', $this->getTags()));
                }
            }

            passthru($cmd);
        }
    }

    /**
     * Runs `git subsplit update`
     */
    public function subsplitUpdate()
    {
        $repo = $this->getRepository();
        $this->log('git-subsplit update...');
        $cmd = $this->client->getCommand('subsplit');
        $cmd->addArgument('update');
        try {
            $cmd->execute();
        } catch (Exception $e) {
            throw new BuildException('git subsplit update failed'. $e);
        }
        chdir($repo . '/.subsplit');
        passthru('php ../composer.phar update --dev');
        chdir($repo);
    }

    /**
     * Runs `git subsplit init` based on the remote repository.
     */
    public function subsplitInit()
    {
        $remote = $this->getRemote();
        $cmd = $this->client->getCommand('subsplit');
        $this->log('running git-subsplit init ' . $remote);

        $cmd->setArguments(array(
            'init',
            $remote
        ));

        try {
            $output = $cmd->execute();
        } catch (Exception $e) {
            throw new BuildException('git subsplit init failed'. $e);
        }
        $this->log(trim($output), Project::MSG_INFO);
        $repo = $this->getRepository();
        chdir($repo . '/.subsplit');
        passthru('php ../composer.phar install --dev');
        chdir($repo);
    }

    /**
     * Find the composer.json files using Phing's directory scanner
     *
     * @return array
     */
    protected function findSplits()
    {
        $this->log("checking heads for subsplits");
        $repo = $this->getRepository();
        $base = $this->getBase();

        $splits = array();
        $heads = $this->getHeads();

        if (!empty($base)) {
            $base = '/' . ltrim($base, '/');
        } else {
            $base = '/';
        }

        chdir($repo . '/.subsplit');
        foreach ($heads as $head) {
            $splits[$head] = array();

            // check each head requested *BEFORE* the actual subtree split command gets it
            passthru("git checkout '$head'");
            $ds = new DirectoryScanner();
            $ds->setBasedir($repo . '/.subsplit' . $base);
            $ds->setIncludes(array('**/'.$this->subIndicatorFile));
            $ds->scan();
            $files = $ds->getIncludedFiles();

            // Process the files we found
            foreach ($files as $file) {
                $pkg = file_get_contents($repo . '/.subsplit' . $base .'/'. $file);
                $pkg_json = json_decode($pkg, true);
                $name = $pkg_json['name'];
                $component = str_replace('/composer.json', '', $file);
                // keep this for split cmd
                $tmpreponame = explode('/', $name);
                $reponame = $tmpreponame[1];
                $splits[$head][$component]['repo'] = $reponame;
                $nscomponent = str_replace('/', '\\', $component);
                $splits[$head][$component]['desc'] = "[READ ONLY] Subtree split of $nscomponent: " . $pkg_json['description'];
            }
        }

        // go back to how we found it
        passthru("git checkout master");
        chdir($repo);
        $this->splits = $splits;
    }

    /**
     * Based on list of repositories we determined we *should* have, talk
     * to GitHub and make sure they're all there.
     *
     */
    protected function verifyRepos()
    {
        $this->log('verifying GitHub target repos');
        $github_org = $this->getOwningTarget()->getProject()->getProperty('github.org');
        $github_creds = $this->getOwningTarget()->getProject()->getProperty('github.basicauth');

        if ($github_creds == 'username:password') {
            $this->log('Skipping GitHub repo checks. Update github.basicauth in build.properties to verify repos.', 1);
            return;
        }

        $ch = curl_init('https://api.github.com/orgs/'.$github_org.'/repos?type=all');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_USERPWD, $github_creds);
        // change this when we know we can use our bundled CA bundle!
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        $result = curl_exec($ch);
        curl_close($ch);
        $repos = json_decode($result, true);
        $existing_repos = array();

        // parse out the repos we found on GitHub
        foreach ($repos as $repo) {
            $tmpreponame = explode('/', $repo['full_name']);
            $reponame = $tmpreponame[1];
            $existing_repos[$reponame] = $repo['description'];
        }

        $heads = $this->getHeads();
        foreach ($heads as $head) {
            foreach ($this->splits[$head] as $component => $meta) {

                $reponame = $meta['repo'];

                if (!isset($existing_repos[$reponame])) {
                    $this->log("Creating missing repo $reponame");
                    $payload = array(
                        'name' => $reponame,
                        'description' => $meta['desc'],
                        'homepage' => 'http://www.guzzlephp.org/',
                        'private' => true,
                        'has_issues' => false,
                        'has_wiki' => false,
                        'has_downloads' => true,
                        'auto_init' => false
                    );
                    $ch = curl_init('https://api.github.com/orgs/'.$github_org.'/repos');
                    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
                    curl_setopt($ch, CURLOPT_USERPWD, $github_creds);
                    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
                    curl_setopt($ch, CURLOPT_POST, 1);
                    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
                    // change this when we know we can use our bundled CA bundle!
                    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
                    $result = curl_exec($ch);
                    echo "Response code: ".curl_getinfo($ch, CURLINFO_HTTP_CODE)."\n";
                    curl_close($ch);
                } else {
                    $this->log("Repo $reponame exists", 2);
                }
            }
        }
    }
}
<?php

namespace Guzzle\Batch;

/**
 * Abstract decorator used when decorating a BatchInterface
 */
abstract class AbstractBatchDecorator implements BatchInterface
{
    /** @var BatchInterface Decorated batch object */
    protected $decoratedBatch;

    /**
     * @param BatchInterface $decoratedBatch  BatchInterface that is being decorated
     */
    public function __construct(BatchInterface $decoratedBatch)
    {
        $this->decoratedBatch = $decoratedBatch;
    }

    /**
     * Allow decorators to implement custom methods
     *
     * @param string $method Missing method name
     * @param array  $args   Method arguments
     *
     * @return mixed
     * @codeCoverageIgnore
     */
    public function __call($method, array $args)
    {
        return call_user_func_array(array($this->decoratedBatch, $method), $args);
    }

    public function add($item)
    {
        $this->decoratedBatch->add($item);

        return $this;
    }

    public function flush()
    {
        return $this->decoratedBatch->flush();
    }

    public function isEmpty()
    {
        return $this->decoratedBatch->isEmpty();
    }

    /**
     * Trace the decorators associated with the batch
     *
     * @return array
     */
    public function getDecorators()
    {
        $found = array($this);
        if (method_exists($this->decoratedBatch, 'getDecorators')) {
            $found = array_merge($found, $this->decoratedBatch->getDecorators());
        }

        return $found;
    }
}
<?php

namespace Guzzle\Batch;

use Guzzle\Batch\Exception\BatchTransferException;

/**
 * Default batch implementation used to convert queued items into smaller chunks of batches using a
 * {@see BatchDivisorIterface} and transfers each batch using a {@see BatchTransferInterface}.
 *
 * Any exception encountered during a flush operation will throw a {@see BatchTransferException} object containing the
 * batch that failed. After an exception is encountered, you can flush the batch again to attempt to finish transferring
 * any previously created batches or queued items.
 */
class Batch implements BatchInterface
{
    /** @var \SplQueue Queue of items in the queue */
    protected $queue;

    /** @var array Divided batches to be transferred */
    protected $dividedBatches;

    /** @var BatchTransferInterface */
    protected $transferStrategy;

    /** @var BatchDivisorInterface */
    protected $divisionStrategy;

    /**
     * @param BatchTransferInterface $transferStrategy Strategy used to transfer items
     * @param BatchDivisorInterface  $divisionStrategy Divisor used to create batches
     */
    public function __construct(BatchTransferInterface $transferStrategy, BatchDivisorInterface $divisionStrategy)
    {
        $this->transferStrategy = $transferStrategy;
        $this->divisionStrategy = $divisionStrategy;
        $this->queue = new \SplQueue();
        $this->queue->setIteratorMode(\SplQueue::IT_MODE_DELETE);
        $this->dividedBatches = array();
    }

    public function add($item)
    {
        $this->queue->enqueue($item);

        return $this;
    }

    public function flush()
    {
        $this->createBatches();

        $items = array();
        foreach ($this->dividedBatches as $batchIndex => $dividedBatch) {
            while ($dividedBatch->valid()) {
                $batch = $dividedBatch->current();
                $dividedBatch->next();
                try {
                    $this->transferStrategy->transfer($batch);
                    $items = array_merge($items, $batch);
                } catch (\Exception $e) {
                    throw new BatchTransferException($batch, $items, $e, $this->transferStrategy, $this->divisionStrategy);
                }
            }
            // Keep the divided batch down to a minimum in case of a later exception
            unset($this->dividedBatches[$batchIndex]);
        }

        return $items;
    }

    public function isEmpty()
    {
        return count($this->queue) == 0 && count($this->dividedBatches) == 0;
    }

    /**
     * Create batches for any queued items
     */
    protected function createBatches()
    {
        if (count($this->queue)) {
            if ($batches = $this->divisionStrategy->createBatches($this->queue)) {
                // Convert arrays into iterators
                if (is_array($batches)) {
                    $batches = new \ArrayIterator($batches);
                }
                $this->dividedBatches[] = $batches;
            }
        }
    }
}
<?php

namespace Guzzle\Batch;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;

/**
 * Builder used to create custom batch objects
 */
class BatchBuilder
{
    /** @var bool Whether or not the batch should automatically flush*/
    protected $autoFlush = false;

    /** @var bool Whether or not to maintain a batch history */
    protected $history = false;

    /** @var bool Whether or not to buffer exceptions encountered in transfer */
    protected $exceptionBuffering = false;

    /** @var mixed Callable to invoke each time a flush completes */
    protected $afterFlush;

    /** @var BatchTransferInterface Object used to transfer items in the queue */
    protected $transferStrategy;

    /** @var BatchDivisorInterface Object used to divide the queue into batches */
    protected $divisorStrategy;

    /** @var array of Mapped transfer strategies by handle name */
    protected static $mapping = array(
        'request' => 'Guzzle\Batch\BatchRequestTransfer',
        'command' => 'Guzzle\Batch\BatchCommandTransfer'
    );

    /**
     * Create a new instance of the BatchBuilder
     *
     * @return BatchBuilder
     */
    public static function factory()
    {
        return new self();
    }

    /**
     * Automatically flush the batch when the size of the queue reaches a certain threshold. Adds {@see FlushingBatch}.
     *
     * @param $threshold Number of items to allow in the queue before a flush
     *
     * @return BatchBuilder
     */
    public function autoFlushAt($threshold)
    {
        $this->autoFlush = $threshold;

        return $this;
    }

    /**
     * Maintain a history of all items that have been transferred using the batch. Adds {@see HistoryBatch}.
     *
     * @return BatchBuilder
     */
    public function keepHistory()
    {
        $this->history = true;

        return $this;
    }

    /**
     * Buffer exceptions thrown during transfer so that you can transfer as much as possible, and after a transfer
     * completes, inspect each exception that was thrown. Enables the {@see ExceptionBufferingBatch} decorator.
     *
     * @return BatchBuilder
     */
    public function bufferExceptions()
    {
        $this->exceptionBuffering = true;

        return $this;
    }

    /**
     * Notify a callable each time a batch flush completes. Enables the {@see NotifyingBatch} decorator.
     *
     * @param mixed $callable Callable function to notify
     *
     * @return BatchBuilder
     * @throws InvalidArgumentException if the argument is not callable
     */
    public function notify($callable)
    {
        $this->afterFlush = $callable;

        return $this;
    }

    /**
     * Configures the batch to transfer batches of requests. Associates a {@see \Guzzle\Http\BatchRequestTransfer}
     * object as both the transfer and divisor strategy.
     *
     * @param int $batchSize Batch size for each batch of requests
     *
     * @return BatchBuilder
     */
    public function transferRequests($batchSize = 50)
    {
        $className = self::$mapping['request'];
        $this->transferStrategy = new $className($batchSize);
        $this->divisorStrategy = $this->transferStrategy;

        return $this;
    }

    /**
     * Configures the batch to transfer batches commands. Associates as
     * {@see \Guzzle\Service\Command\BatchCommandTransfer} as both the transfer and divisor strategy.
     *
     * @param int $batchSize Batch size for each batch of commands
     *
     * @return BatchBuilder
     */
    public function transferCommands($batchSize = 50)
    {
        $className = self::$mapping['command'];
        $this->transferStrategy = new $className($batchSize);
        $this->divisorStrategy = $this->transferStrategy;

        return $this;
    }

    /**
     * Specify the strategy used to divide the queue into an array of batches
     *
     * @param BatchDivisorInterface $divisorStrategy Strategy used to divide a batch queue into batches
     *
     * @return BatchBuilder
     */
    public function createBatchesWith(BatchDivisorInterface $divisorStrategy)
    {
        $this->divisorStrategy = $divisorStrategy;

        return $this;
    }

    /**
     * Specify the strategy used to transport the items when flush is called
     *
     * @param BatchTransferInterface $transferStrategy How items are transferred
     *
     * @return BatchBuilder
     */
    public function transferWith(BatchTransferInterface $transferStrategy)
    {
        $this->transferStrategy = $transferStrategy;

        return $this;
    }

    /**
     * Create and return the instantiated batch
     *
     * @return BatchInterface
     * @throws RuntimeException if no transfer strategy has been specified
     */
    public function build()
    {
        if (!$this->transferStrategy) {
            throw new RuntimeException('No transfer strategy has been specified');
        }

        if (!$this->divisorStrategy) {
            throw new RuntimeException('No divisor strategy has been specified');
        }

        $batch = new Batch($this->transferStrategy, $this->divisorStrategy);

        if ($this->exceptionBuffering) {
            $batch = new ExceptionBufferingBatch($batch);
        }

        if ($this->afterFlush) {
            $batch = new NotifyingBatch($batch, $this->afterFlush);
        }

        if ($this->autoFlush) {
            $batch = new FlushingBatch($batch, $this->autoFlush);
        }

        if ($this->history) {
            $batch = new HistoryBatch($batch);
        }

        return $batch;
    }
}
<?php

namespace Guzzle\Batch;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * Divides batches using a callable
 */
class BatchClosureDivisor implements BatchDivisorInterface
{
    /** @var callable Method used to divide the batches */
    protected $callable;

    /** @var mixed $context Context passed to the callable */
    protected $context;

    /**
     * @param callable $callable Method used to divide the batches. The method must accept an \SplQueue and return an
     *                           array of arrays containing the divided items.
     * @param mixed    $context  Optional context to pass to the batch divisor
     *
     * @throws InvalidArgumentException if the callable is not callable
     */
    public function __construct($callable, $context = null)
    {
        if (!is_callable($callable)) {
            throw new InvalidArgumentException('Must pass a callable');
        }

        $this->callable = $callable;
        $this->context = $context;
    }

    public function createBatches(\SplQueue $queue)
    {
        return call_user_func($this->callable, $queue, $this->context);
    }
}
<?php

namespace Guzzle\Batch;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * Batch transfer strategy where transfer logic can be defined via a Closure.
 * This class is to be used with {@see Guzzle\Batch\BatchInterface}
 */
class BatchClosureTransfer implements BatchTransferInterface
{
    /** @var callable A closure that performs the transfer */
    protected $callable;

    /** @var mixed $context Context passed to the callable */
    protected $context;

    /**
     * @param mixed $callable Callable that performs the transfer. This function should accept two arguments:
     *                        (array $batch, mixed $context).
     * @param mixed $context  Optional context to pass to the batch divisor
     *
     * @throws InvalidArgumentException
     */
    public function __construct($callable, $context = null)
    {
        if (!is_callable($callable)) {
            throw new InvalidArgumentException('Argument must be callable');
        }

        $this->callable = $callable;
        $this->context = $context;
    }

    public function transfer(array $batch)
    {
        return empty($batch) ? null : call_user_func($this->callable, $batch, $this->context);
    }
}
<?php

namespace Guzzle\Batch;

use Guzzle\Batch\BatchTransferInterface;
use Guzzle\Batch\BatchDivisorInterface;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Exception\InconsistentClientTransferException;

/**
 * Efficiently transfers multiple commands in parallel per client
 * This class is to be used with {@see Guzzle\Batch\BatchInterface}
 */
class BatchCommandTransfer implements BatchTransferInterface, BatchDivisorInterface
{
    /** @var int Size of each command batch */
    protected $batchSize;

    /**
     * @param int $batchSize Size of each batch
     */
    public function __construct($batchSize = 50)
    {
        $this->batchSize = $batchSize;
    }

    /**
     * Creates batches by grouping commands by their associated client
     * {@inheritdoc}
     */
    public function createBatches(\SplQueue $queue)
    {
        $groups = new \SplObjectStorage();
        foreach ($queue as $item) {
            if (!$item instanceof CommandInterface) {
                throw new InvalidArgumentException('All items must implement Guzzle\Service\Command\CommandInterface');
            }
            $client = $item->getClient();
            if (!$groups->contains($client)) {
                $groups->attach($client, new \ArrayObject(array($item)));
            } else {
                $groups[$client]->append($item);
            }
        }

        $batches = array();
        foreach ($groups as $batch) {
            $batches = array_merge($batches, array_chunk($groups[$batch]->getArrayCopy(), $this->batchSize));
        }

        return $batches;
    }

    public function transfer(array $batch)
    {
        if (empty($batch)) {
            return;
        }

        // Get the client of the first found command
        $client = reset($batch)->getClient();

        // Keep a list of all commands with invalid clients
        $invalid = array_filter($batch, function ($command) use ($client) {
            return $command->getClient() !== $client;
        });

        if (!empty($invalid)) {
            throw new InconsistentClientTransferException($invalid);
        }

        $client->execute($batch);
    }
}
<?php

namespace Guzzle\Batch;

/**
 * Interface used for dividing a queue of items into an array of batches
 */
interface BatchDivisorInterface
{
    /**
     * Divide a queue of items into an array batches
     *
     * @param \SplQueue $queue Queue of items to divide into batches. Items are removed as they are iterated.
     *
     * @return array|\Traversable Returns an array or Traversable object that contains arrays of items to transfer
     */
    public function createBatches(\SplQueue $queue);
}
<?php

namespace Guzzle\Batch;

/**
 * Interface for efficiently transferring items in a queue using batches
 */
interface BatchInterface
{
    /**
     * Add an item to the queue
     *
     * @param mixed $item Item to add
     *
     * @return self
     */
    public function add($item);

    /**
     * Flush the batch and transfer the items
     *
     * @return array Returns an array flushed items
     */
    public function flush();

    /**
     * Check if the batch is empty and has further items to transfer
     *
     * @return bool
     */
    public function isEmpty();
}
<?php

namespace Guzzle\Batch;

use Guzzle\Batch\BatchTransferInterface;
use Guzzle\Batch\BatchDivisorInterface;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Message\RequestInterface;

/**
 * Batch transfer strategy used to efficiently transfer a batch of requests.
 * This class is to be used with {@see Guzzle\Batch\BatchInterface}
 */
class BatchRequestTransfer implements BatchTransferInterface, BatchDivisorInterface
{
    /** @var int Size of each command batch */
    protected $batchSize;

    /**
     * Constructor used to specify how large each batch should be
     *
     * @param int $batchSize Size of each batch
     */
    public function __construct($batchSize = 50)
    {
        $this->batchSize = $batchSize;
    }

    /**
     * Creates batches of requests by grouping requests by their associated curl multi object.
     * {@inheritdoc}
     */
    public function createBatches(\SplQueue $queue)
    {
        // Create batches by client objects
        $groups = new \SplObjectStorage();
        foreach ($queue as $item) {
            if (!$item instanceof RequestInterface) {
                throw new InvalidArgumentException('All items must implement Guzzle\Http\Message\RequestInterface');
            }
            $client = $item->getClient();
            if (!$groups->contains($client)) {
                $groups->attach($client, array($item));
            } else {
                $current = $groups[$client];
                $current[] = $item;
                $groups[$client] = $current;
            }
        }

        $batches = array();
        foreach ($groups as $batch) {
            $batches = array_merge($batches, array_chunk($groups[$batch], $this->batchSize));
        }

        return $batches;
    }

    public function transfer(array $batch)
    {
        if ($batch) {
            reset($batch)->getClient()->send($batch);
        }
    }
}
<?php

namespace Guzzle\Batch;

/**
 * Divides batches into smaller batches under a certain size
 */
class BatchSizeDivisor implements BatchDivisorInterface
{
    /** @var int Size of each batch */
    protected $size;

    /** @param int $size Size of each batch */
    public function __construct($size)
    {
        $this->size = $size;
    }

    /**
     * Set the size of each batch
     *
     * @param int $size Size of each batch
     *
     * @return BatchSizeDivisor
     */
    public function setSize($size)
    {
        $this->size = $size;

        return $this;
    }

    /**
     * Get the size of each batch
     *
     * @return int
     */
    public function getSize()
    {
        return $this->size;
    }

    public function createBatches(\SplQueue $queue)
    {
        return array_chunk(iterator_to_array($queue, false), $this->size);
    }
}
<?php

namespace Guzzle\Batch;

/**
 * Interface used for transferring batches of items
 */
interface BatchTransferInterface
{
    /**
     * Transfer an array of items
     *
     * @param array $batch Array of items to transfer
     */
    public function transfer(array $batch);
}
<?php

namespace Guzzle\Batch\Exception;

use Guzzle\Common\Exception\GuzzleException;
use Guzzle\Batch\BatchTransferInterface as TransferStrategy;
use Guzzle\Batch\BatchDivisorInterface as DivisorStrategy;

/**
 * Exception thrown during a batch transfer
 */
class BatchTransferException extends \Exception implements GuzzleException
{
    /** @var array The batch being sent when the exception occurred */
    protected $batch;

    /** @var TransferStrategy The transfer strategy in use when the exception occurred */
    protected $transferStrategy;

    /** @var DivisorStrategy The divisor strategy in use when the exception occurred */
    protected $divisorStrategy;

    /** @var array Items transferred at the point in which the exception was encountered */
    protected $transferredItems;

    /**
     * @param array            $batch            The batch being sent when the exception occurred
     * @param array            $transferredItems Items transferred at the point in which the exception was encountered
     * @param \Exception       $exception        Exception encountered
     * @param TransferStrategy $transferStrategy The transfer strategy in use when the exception occurred
     * @param DivisorStrategy  $divisorStrategy  The divisor strategy in use when the exception occurred
     */
    public function __construct(
        array $batch,
        array $transferredItems,
        \Exception $exception,
        TransferStrategy $transferStrategy = null,
        DivisorStrategy $divisorStrategy = null
    ) {
        $this->batch = $batch;
        $this->transferredItems = $transferredItems;
        $this->transferStrategy = $transferStrategy;
        $this->divisorStrategy = $divisorStrategy;
        parent::__construct(
            'Exception encountered while transferring batch: ' . $exception->getMessage(),
            $exception->getCode(),
            $exception
        );
    }

    /**
     * Get the batch that we being sent when the exception occurred
     *
     * @return array
     */
    public function getBatch()
    {
        return $this->batch;
    }

    /**
     * Get the items transferred at the point in which the exception was encountered
     *
     * @return array
     */
    public function getTransferredItems()
    {
        return $this->transferredItems;
    }

    /**
     * Get the transfer strategy
     *
     * @return TransferStrategy
     */
    public function getTransferStrategy()
    {
        return $this->transferStrategy;
    }

    /**
     * Get the divisor strategy
     *
     * @return DivisorStrategy
     */
    public function getDivisorStrategy()
    {
        return $this->divisorStrategy;
    }
}
<?php

namespace Guzzle\Batch;

use Guzzle\Batch\Exception\BatchTransferException;

/**
 * BatchInterface decorator used to buffer exceptions encountered during a transfer.  The exceptions can then later be
 * processed after a batch flush has completed.
 */
class ExceptionBufferingBatch extends AbstractBatchDecorator
{
    /** @var array Array of BatchTransferException exceptions */
    protected $exceptions = array();

    public function flush()
    {
        $items = array();

        while (!$this->decoratedBatch->isEmpty()) {
            try {
                $transferredItems = $this->decoratedBatch->flush();
            } catch (BatchTransferException $e) {
                $this->exceptions[] = $e;
                $transferredItems = $e->getTransferredItems();
            }
            $items = array_merge($items, $transferredItems);
        }

        return $items;
    }

    /**
     * Get the buffered exceptions
     *
     * @return array Array of BatchTransferException objects
     */
    public function getExceptions()
    {
        return $this->exceptions;
    }

    /**
     * Clear the buffered exceptions
     */
    public function clearExceptions()
    {
        $this->exceptions = array();
    }
}
<?php

namespace Guzzle\Batch;

/**
 * BatchInterface decorator used to add automatic flushing of the queue when the size of the queue reaches a threshold.
 */
class FlushingBatch extends AbstractBatchDecorator
{
    /** @var int The threshold for which to automatically flush */
    protected $threshold;

    /** @var int Current number of items known to be in the queue */
    protected $currentTotal = 0;

    /**
     * @param BatchInterface $decoratedBatch  BatchInterface that is being decorated
     * @param int            $threshold       Flush when the number in queue matches the threshold
     */
    public function __construct(BatchInterface $decoratedBatch, $threshold)
    {
        $this->threshold = $threshold;
        parent::__construct($decoratedBatch);
    }

    /**
     * Set the auto-flush threshold
     *
     * @param int $threshold The auto-flush threshold
     *
     * @return FlushingBatch
     */
    public function setThreshold($threshold)
    {
        $this->threshold = $threshold;

        return $this;
    }

    /**
     * Get the auto-flush threshold
     *
     * @return int
     */
    public function getThreshold()
    {
        return $this->threshold;
    }

    public function add($item)
    {
        $this->decoratedBatch->add($item);
        if (++$this->currentTotal >= $this->threshold) {
            $this->currentTotal = 0;
            $this->decoratedBatch->flush();
        }

        return $this;
    }
}
<?php

namespace Guzzle\Batch;

/**
 * BatchInterface decorator used to keep a history of items that were added to the batch.  You must clear the history
 * manually to remove items from the history.
 */
class HistoryBatch extends AbstractBatchDecorator
{
    /** @var array Items in the history */
    protected $history = array();

    public function add($item)
    {
        $this->history[] = $item;
        $this->decoratedBatch->add($item);

        return $this;
    }

    /**
     * Get the batch history
     *
     * @return array
     */
    public function getHistory()
    {
        return $this->history;
    }

    /**
     * Clear the batch history
     */
    public function clearHistory()
    {
        $this->history = array();
    }
}
<?php

namespace Guzzle\Batch;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * BatchInterface decorator used to call a method each time flush is called
 */
class NotifyingBatch extends AbstractBatchDecorator
{
    /** @var mixed Callable to call */
    protected $callable;

    /**
     * @param BatchInterface $decoratedBatch Batch object to decorate
     * @param mixed          $callable       Callable to call
     *
     * @throws InvalidArgumentException
     */
    public function __construct(BatchInterface $decoratedBatch, $callable)
    {
        if (!is_callable($callable)) {
            throw new InvalidArgumentException('The passed argument is not callable');
        }

        $this->callable = $callable;
        parent::__construct($decoratedBatch);
    }

    public function flush()
    {
        $items = $this->decoratedBatch->flush();
        call_user_func($this->callable, $items);

        return $items;
    }
}
<?php

namespace Guzzle\Cache;

/**
 * Abstract cache adapter
 */
abstract class AbstractCacheAdapter implements CacheAdapterInterface
{
    protected $cache;

    /**
     * Get the object owned by the adapter
     *
     * @return mixed
     */
    public function getCacheObject()
    {
        return $this->cache;
    }
}
<?php

namespace Guzzle\Cache;

use Doctrine\Common\Cache\Cache;
use Guzzle\Common\Version;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Common\FromConfigInterface;
use Zend\Cache\Storage\StorageInterface;

/**
 * Generates cache adapters from any number of known cache implementations
 */
class CacheAdapterFactory implements FromConfigInterface
{
    /**
     * Create a Guzzle cache adapter based on an array of options
     *
     * @param mixed $cache Cache value
     *
     * @return CacheAdapterInterface
     * @throws InvalidArgumentException
     */
    public static function fromCache($cache)
    {
        if (!is_object($cache)) {
            throw new InvalidArgumentException('Cache must be one of the known cache objects');
        }

        if ($cache instanceof CacheAdapterInterface) {
            return $cache;
        } elseif ($cache instanceof Cache) {
            return new DoctrineCacheAdapter($cache);
        } elseif ($cache instanceof StorageInterface) {
            return new Zf2CacheAdapter($cache);
        } else {
            throw new InvalidArgumentException('Unknown cache type: ' . get_class($cache));
        }
    }

    /**
     * Create a Guzzle cache adapter based on an array of options
     *
     * @param array $config Array of configuration options
     *
     * @return CacheAdapterInterface
     * @throws InvalidArgumentException
     * @deprecated This will be removed in a future version
     * @codeCoverageIgnore
     */
    public static function factory($config = array())
    {
        Version::warn(__METHOD__ . ' is deprecated');
        if (!is_array($config)) {
            throw new InvalidArgumentException('$config must be an array');
        }

        if (!isset($config['cache.adapter']) && !isset($config['cache.provider'])) {
            $config['cache.adapter'] = 'Guzzle\Cache\NullCacheAdapter';
            $config['cache.provider'] = null;
        } else {
            // Validate that the options are valid
            foreach (array('cache.adapter', 'cache.provider') as $required) {
                if (!isset($config[$required])) {
                    throw new InvalidArgumentException("{$required} is a required CacheAdapterFactory option");
                }
                if (is_string($config[$required])) {
                    // Convert dot notation to namespaces
                    $config[$required] = str_replace('.', '\\', $config[$required]);
                    if (!class_exists($config[$required])) {
                        throw new InvalidArgumentException("{$config[$required]} is not a valid class for {$required}");
                    }
                }
            }
            // Instantiate the cache provider
            if (is_string($config['cache.provider'])) {
                $args = isset($config['cache.provider.args']) ? $config['cache.provider.args'] : null;
                $config['cache.provider'] = self::createObject($config['cache.provider'], $args);
            }
        }

        // Instantiate the cache adapter using the provider and options
        if (is_string($config['cache.adapter'])) {
            $args = isset($config['cache.adapter.args']) ? $config['cache.adapter.args'] : array();
            array_unshift($args, $config['cache.provider']);
            $config['cache.adapter'] = self::createObject($config['cache.adapter'], $args);
        }

        return $config['cache.adapter'];
    }

    /**
     * Create a class using an array of constructor arguments
     *
     * @param string $className Class name
     * @param array  $args      Arguments for the class constructor
     *
     * @return mixed
     * @throws RuntimeException
     * @deprecated
     * @codeCoverageIgnore
     */
    private static function createObject($className, array $args = null)
    {
        try {
            if (!$args) {
                return new $className;
            } else {
                $c = new \ReflectionClass($className);
                return $c->newInstanceArgs($args);
            }
        } catch (\Exception $e) {
            throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
        }
    }
}
<?php

namespace Guzzle\Cache;

/**
 * Interface for cache adapters.
 *
 * Cache adapters allow Guzzle to utilize various frameworks for caching HTTP responses.
 *
 * @link http://www.doctrine-project.org/ Inspired by Doctrine 2
 */
interface CacheAdapterInterface
{
    /**
     * Test if an entry exists in the cache.
     *
     * @param string $id      cache id The cache id of the entry to check for.
     * @param array  $options Array of cache adapter options
     *
     * @return bool Returns TRUE if a cache entry exists for the given cache id, FALSE otherwise.
     */
    public function contains($id, array $options = null);

    /**
     * Deletes a cache entry.
     *
     * @param string $id      cache id
     * @param array  $options Array of cache adapter options
     *
     * @return bool TRUE on success, FALSE on failure
     */
    public function delete($id, array $options = null);

    /**
     * Fetches an entry from the cache.
     *
     * @param string $id      cache id The id of the cache entry to fetch.
     * @param array  $options Array of cache adapter options
     *
     * @return string The cached data or FALSE, if no cache entry exists for the given id.
     */
    public function fetch($id, array $options = null);

    /**
     * Puts data into the cache.
     *
     * @param string   $id       The cache id
     * @param string   $data     The cache entry/data
     * @param int|bool $lifeTime The lifetime. If != false, sets a specific lifetime for this cache entry
     * @param array    $options  Array of cache adapter options
     *
     * @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise.
     */
    public function save($id, $data, $lifeTime = false, array $options = null);
}
<?php

namespace Guzzle\Cache;

/**
 * Cache adapter that defers to closures for implementation
 */
class ClosureCacheAdapter implements CacheAdapterInterface
{
    /**
     * @var array Mapping of method names to callables
     */
    protected $callables;

    /**
     * The callables array is an array mapping the actions of the cache adapter to callables.
     * - contains: Callable that accepts an $id and $options argument
     * - delete:   Callable that accepts an $id and $options argument
     * - fetch:    Callable that accepts an $id and $options argument
     * - save:     Callable that accepts an $id, $data, $lifeTime, and $options argument
     *
     * @param array $callables array of action names to callable
     *
     * @throws \InvalidArgumentException if the callable is not callable
     */
    public function __construct(array $callables)
    {
        // Validate each key to ensure it exists and is callable
        foreach (array('contains', 'delete', 'fetch', 'save') as $key) {
            if (!array_key_exists($key, $callables) || !is_callable($callables[$key])) {
                throw new \InvalidArgumentException("callables must contain a callable {$key} key");
            }
        }

        $this->callables = $callables;
    }

    public function contains($id, array $options = null)
    {
        return call_user_func($this->callables['contains'], $id, $options);
    }

    public function delete($id, array $options = null)
    {
        return call_user_func($this->callables['delete'], $id, $options);
    }

    public function fetch($id, array $options = null)
    {
        return call_user_func($this->callables['fetch'], $id, $options);
    }

    public function save($id, $data, $lifeTime = false, array $options = null)
    {
        return call_user_func($this->callables['save'], $id, $data, $lifeTime, $options);
    }
}
<?php

namespace Guzzle\Cache;

use Doctrine\Common\Cache\Cache;

/**
 * Doctrine 2 cache adapter
 *
 * @link http://www.doctrine-project.org/
 */
class DoctrineCacheAdapter extends AbstractCacheAdapter
{
    /**
     * @param Cache $cache Doctrine cache object
     */
    public function __construct(Cache $cache)
    {
        $this->cache = $cache;
    }

    public function contains($id, array $options = null)
    {
        return $this->cache->contains($id);
    }

    public function delete($id, array $options = null)
    {
        return $this->cache->delete($id);
    }

    public function fetch($id, array $options = null)
    {
        return $this->cache->fetch($id);
    }

    public function save($id, $data, $lifeTime = false, array $options = null)
    {
        return $this->cache->save($id, $data, $lifeTime !== false ? $lifeTime : 0);
    }
}
<?php

namespace Guzzle\Cache;

/**
 * Null cache adapter
 */
class NullCacheAdapter extends AbstractCacheAdapter
{
    public function __construct() {}

    public function contains($id, array $options = null)
    {
        return false;
    }

    public function delete($id, array $options = null)
    {
        return true;
    }

    public function fetch($id, array $options = null)
    {
        return false;
    }

    public function save($id, $data, $lifeTime = false, array $options = null)
    {
        return true;
    }
}
<?php

namespace Guzzle\Cache;

use Guzzle\Common\Version;

/**
 * Zend Framework 1 cache adapter
 *
 * @link http://framework.zend.com/manual/en/zend.cache.html
 * @deprecated
 * @codeCoverageIgnore
 */
class Zf1CacheAdapter extends AbstractCacheAdapter
{
    /**
     * @param \Zend_Cache_Backend $cache Cache object to wrap
     */
    public function __construct(\Zend_Cache_Backend $cache)
    {
        Version::warn(__CLASS__ . ' is deprecated. Upgrade to ZF2 or use PsrCacheAdapter');
        $this->cache = $cache;
    }

    public function contains($id, array $options = null)
    {
        return $this->cache->test($id);
    }

    public function delete($id, array $options = null)
    {
        return $this->cache->remove($id);
    }

    public function fetch($id, array $options = null)
    {
        return $this->cache->load($id);
    }

    public function save($id, $data, $lifeTime = false, array $options = null)
    {
        return $this->cache->save($data, $id, array(), $lifeTime);
    }
}
<?php

namespace Guzzle\Cache;

use Zend\Cache\Storage\StorageInterface;

/**
 * Zend Framework 2 cache adapter
 *
 * @link http://packages.zendframework.com/docs/latest/manual/en/zend.cache.html
 */
class Zf2CacheAdapter extends AbstractCacheAdapter
{
    /**
     * @param StorageInterface $cache Zend Framework 2 cache adapter
     */
    public function __construct(StorageInterface $cache)
    {
        $this->cache = $cache;
    }

    public function contains($id, array $options = null)
    {
        return $this->cache->hasItem($id);
    }

    public function delete($id, array $options = null)
    {
        return $this->cache->removeItem($id);
    }

    public function fetch($id, array $options = null)
    {
        return $this->cache->getItem($id);
    }

    public function save($id, $data, $lifeTime = false, array $options = null)
    {
        return $this->cache->setItem($id, $data);
    }
}
<?php

namespace Guzzle\Common;

use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Class that holds an event dispatcher
 */
class AbstractHasDispatcher implements HasDispatcherInterface
{
    /** @var EventDispatcherInterface */
    protected $eventDispatcher;

    public static function getAllEvents()
    {
        return array();
    }

    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
    {
        $this->eventDispatcher = $eventDispatcher;

        return $this;
    }

    public function getEventDispatcher()
    {
        if (!$this->eventDispatcher) {
            $this->eventDispatcher = new EventDispatcher();
        }

        return $this->eventDispatcher;
    }

    public function dispatch($eventName, array $context = array())
    {
        return $this->getEventDispatcher()->dispatch($eventName, new Event($context));
    }

    public function addSubscriber(EventSubscriberInterface $subscriber)
    {
        $this->getEventDispatcher()->addSubscriber($subscriber);

        return $this;
    }
}
<?php

namespace Guzzle\Common;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;

/**
 * Key value pair collection object
 */
class Collection implements \ArrayAccess, \IteratorAggregate, \Countable, ToArrayInterface
{
    /** @var array Data associated with the object. */
    protected $data;

    /**
     * @param array $data Associative array of data to set
     */
    public function __construct(array $data = array())
    {
        $this->data = $data;
    }

    /**
     * Create a new collection from an array, validate the keys, and add default values where missing
     *
     * @param array $config   Configuration values to apply.
     * @param array $defaults Default parameters
     * @param array $required Required parameter names
     *
     * @return self
     * @throws InvalidArgumentException if a parameter is missing
     */
    public static function fromConfig(array $config = array(), array $defaults = array(), array $required = array())
    {
        $data = $config + $defaults;

        if ($missing = array_diff($required, array_keys($data))) {
            throw new InvalidArgumentException('Config is missing the following keys: ' . implode(', ', $missing));
        }

        return new self($data);
    }

    public function count()
    {
        return count($this->data);
    }

    public function getIterator()
    {
        return new \ArrayIterator($this->data);
    }

    public function toArray()
    {
        return $this->data;
    }

    /**
     * Removes all key value pairs
     *
     * @return Collection
     */
    public function clear()
    {
        $this->data = array();

        return $this;
    }

    /**
     * Get all or a subset of matching key value pairs
     *
     * @param array $keys Pass an array of keys to retrieve only a subset of key value pairs
     *
     * @return array Returns an array of all matching key value pairs
     */
    public function getAll(array $keys = null)
    {
        return $keys ? array_intersect_key($this->data, array_flip($keys)) : $this->data;
    }

    /**
     * Get a specific key value.
     *
     * @param string $key Key to retrieve.
     *
     * @return mixed|null Value of the key or NULL
     */
    public function get($key)
    {
        return isset($this->data[$key]) ? $this->data[$key] : null;
    }

    /**
     * Set a key value pair
     *
     * @param string $key   Key to set
     * @param mixed  $value Value to set
     *
     * @return Collection Returns a reference to the object
     */
    public function set($key, $value)
    {
        $this->data[$key] = $value;

        return $this;
    }

    /**
     * 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
     * array and the new value will be pushed to the end of the array.
     *
     * @param string $key   Key to add
     * @param mixed  $value Value to add to the key
     *
     * @return Collection Returns a reference to the object.
     */
    public function add($key, $value)
    {
        if (!array_key_exists($key, $this->data)) {
            $this->data[$key] = $value;
        } elseif (is_array($this->data[$key])) {
            $this->data[$key][] = $value;
        } else {
            $this->data[$key] = array($this->data[$key], $value);
        }

        return $this;
    }

    /**
     * Remove a specific key value pair
     *
     * @param string $key A key to remove
     *
     * @return Collection
     */
    public function remove($key)
    {
        unset($this->data[$key]);

        return $this;
    }

    /**
     * Get all keys in the collection
     *
     * @return array
     */
    public function getKeys()
    {
        return array_keys($this->data);
    }

    /**
     * Returns whether or not the specified key is present.
     *
     * @param string $key The key for which to check the existence.
     *
     * @return bool
     */
    public function hasKey($key)
    {
        return array_key_exists($key, $this->data);
    }

    /**
     * Case insensitive search the keys in the collection
     *
     * @param string $key Key to search for
     *
     * @return bool|string Returns false if not found, otherwise returns the key
     */
    public function keySearch($key)
    {
        foreach (array_keys($this->data) as $k) {
            if (!strcasecmp($k, $key)) {
                return $k;
            }
        }

        return false;
    }

    /**
     * Checks if any keys contains a certain value
     *
     * @param string $value Value to search for
     *
     * @return mixed Returns the key if the value was found FALSE if the value was not found.
     */
    public function hasValue($value)
    {
        return array_search($value, $this->data);
    }

    /**
     * Replace the data of the object with the value of an array
     *
     * @param array $data Associative array of data
     *
     * @return Collection Returns a reference to the object
     */
    public function replace(array $data)
    {
        $this->data = $data;

        return $this;
    }

    /**
     * Add and merge in a Collection or array of key value pair data.
     *
     * @param Collection|array $data Associative array of key value pair data
     *
     * @return Collection Returns a reference to the object.
     */
    public function merge($data)
    {
        foreach ($data as $key => $value) {
            $this->add($key, $value);
        }

        return $this;
    }

    /**
     * Over write key value pairs in this collection with all of the data from an array or collection.
     *
     * @param array|\Traversable $data Values to override over this config
     *
     * @return self
     */
    public function overwriteWith($data)
    {
        if (is_array($data)) {
            $this->data = $data + $this->data;
        } elseif ($data instanceof Collection) {
            $this->data = $data->toArray() + $this->data;
        } else {
            foreach ($data as $key => $value) {
                $this->data[$key] = $value;
            }
        }

        return $this;
    }

    /**
     * Returns a Collection containing all the elements of the collection after applying the callback function to each
     * one. The Closure should accept three parameters: (string) $key, (string) $value, (array) $context and return a
     * modified value
     *
     * @param \Closure $closure Closure to apply
     * @param array    $context Context to pass to the closure
     * @param bool     $static  Set to TRUE to use the same class as the return rather than returning a Collection
     *
     * @return Collection
     */
    public function map(\Closure $closure, array $context = array(), $static = true)
    {
        $collection = $static ? new static() : new self();
        foreach ($this as $key => $value) {
            $collection->add($key, $closure($key, $value, $context));
        }

        return $collection;
    }

    /**
     * Iterates over each key value pair in the collection passing them to the Closure. If the  Closure function returns
     * true, the current value from input is returned into the result Collection.  The Closure must accept three
     * parameters: (string) $key, (string) $value and return Boolean TRUE or FALSE for each value.
     *
     * @param \Closure $closure Closure evaluation function
     * @param bool     $static  Set to TRUE to use the same class as the return rather than returning a Collection
     *
     * @return Collection
     */
    public function filter(\Closure $closure, $static = true)
    {
        $collection = ($static) ? new static() : new self();
        foreach ($this->data as $key => $value) {
            if ($closure($key, $value)) {
                $collection->add($key, $value);
            }
        }

        return $collection;
    }

    public function offsetExists($offset)
    {
        return isset($this->data[$offset]);
    }

    public function offsetGet($offset)
    {
        return isset($this->data[$offset]) ? $this->data[$offset] : null;
    }

    public function offsetSet($offset, $value)
    {
        $this->data[$offset] = $value;
    }

    public function offsetUnset($offset)
    {
        unset($this->data[$offset]);
    }

    /**
     * Set a value into a nested array key. Keys will be created as needed to set the value.
     *
     * @param string $path  Path to set
     * @param mixed  $value Value to set at the key
     *
     * @return self
     * @throws RuntimeException when trying to setPath using a nested path that travels through a scalar value
     */
    public function setPath($path, $value)
    {
        $current =& $this->data;
        $queue = explode('/', $path);
        while (null !== ($key = array_shift($queue))) {
            if (!is_array($current)) {
                throw new RuntimeException("Trying to setPath {$path}, but {$key} is set and is not an array");
            } elseif (!$queue) {
                $current[$key] = $value;
            } elseif (isset($current[$key])) {
                $current =& $current[$key];
            } else {
                $current[$key] = array();
                $current =& $current[$key];
            }
        }

        return $this;
    }

    /**
     * Gets a value from the collection using an array path (e.g. foo/baz/bar would retrieve bar from two nested arrays)
     * Allows for wildcard searches which recursively combine matches up to the level at which the wildcard occurs. This
     * can be useful for accepting any key of a sub-array and combining matching keys from each diverging path.
     *
     * @param string $path      Path to traverse and retrieve a value from
     * @param string $separator Character used to add depth to the search
     * @param mixed  $data      Optional data to descend into (used when wildcards are encountered)
     *
     * @return mixed|null
     */
    public function getPath($path, $separator = '/', $data = null)
    {
        if ($data === null) {
            $data =& $this->data;
        }

        $path = is_array($path) ? $path : explode($separator, $path);
        while (null !== ($part = array_shift($path))) {
            if (!is_array($data)) {
                return null;
            } elseif (isset($data[$part])) {
                $data =& $data[$part];
            } elseif ($part != '*') {
                return null;
            } else {
                // Perform a wildcard search by diverging and merging paths
                $result = array();
                foreach ($data as $value) {
                    if (!$path) {
                        $result = array_merge_recursive($result, (array) $value);
                    } elseif (null !== ($test = $this->getPath($path, $separator, $value))) {
                        $result = array_merge_recursive($result, (array) $test);
                    }
                }
                return $result;
            }
        }

        return $data;
    }

    /**
     * Inject configuration settings into an input string
     *
     * @param string $input Input to inject
     *
     * @return string
     * @deprecated
     */
    public function inject($input)
    {
        Version::warn(__METHOD__ . ' is deprecated');
        $replace = array();
        foreach ($this->data as $key => $val) {
            $replace['{' . $key . '}'] = $val;
        }

        return strtr($input, $replace);
    }
}
<?php

namespace Guzzle\Common;

use Symfony\Component\EventDispatcher\Event as SymfonyEvent;

/**
 * Default event for Guzzle notifications
 */
class Event extends SymfonyEvent implements ToArrayInterface, \ArrayAccess, \IteratorAggregate
{
    /** @var array */
    private $context;

    /**
     * @param array $context Contextual information
     */
    public function __construct(array $context = array())
    {
        $this->context = $context;
    }

    public function getIterator()
    {
        return new \ArrayIterator($this->context);
    }

    public function offsetGet($offset)
    {
        return isset($this->context[$offset]) ? $this->context[$offset] : null;
    }

    public function offsetSet($offset, $value)
    {
        $this->context[$offset] = $value;
    }

    public function offsetExists($offset)
    {
        return isset($this->context[$offset]);
    }

    public function offsetUnset($offset)
    {
        unset($this->context[$offset]);
    }

    public function toArray()
    {
        return $this->context;
    }
}
<?php

namespace Guzzle\Common\Exception;

class BadMethodCallException extends \BadMethodCallException implements GuzzleException {}
<?php

namespace Guzzle\Common\Exception;

/**
 * Collection of exceptions
 */
class ExceptionCollection extends \Exception implements GuzzleException, \IteratorAggregate, \Countable
{
    /** @var array Array of Exceptions */
    protected $exceptions = array();

    /** @var string Succinct exception message not including sub-exceptions */
    private $shortMessage;

    public function __construct($message = '', $code = 0, \Exception $previous = null)
    {
        parent::__construct($message, $code, $previous);
        $this->shortMessage = $message;
    }

    /**
     * Set all of the exceptions
     *
     * @param array $exceptions Array of exceptions
     *
     * @return self
     */
    public function setExceptions(array $exceptions)
    {
        $this->exceptions = array();
        foreach ($exceptions as $exception) {
            $this->add($exception);
        }

        return $this;
    }

    /**
     * Add exceptions to the collection
     *
     * @param ExceptionCollection|\Exception $e Exception to add
     *
     * @return ExceptionCollection;
     */
    public function add($e)
    {
        $this->exceptions[] = $e;
        if ($this->message) {
            $this->message .= "\n";
        }

        $this->message .= $this->getExceptionMessage($e, 0);

        return $this;
    }

    /**
     * Get the total number of request exceptions
     *
     * @return int
     */
    public function count()
    {
        return count($this->exceptions);
    }

    /**
     * Allows array-like iteration over the request exceptions
     *
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->exceptions);
    }

    /**
     * Get the first exception in the collection
     *
     * @return \Exception
     */
    public function getFirst()
    {
        return $this->exceptions ? $this->exceptions[0] : null;
    }

    private function getExceptionMessage(\Exception $e, $depth = 0)
    {
        static $sp = '    ';
        $prefix = $depth ? str_repeat($sp, $depth) : '';
        $message = "{$prefix}(" . get_class($e) . ') ' . $e->getFile() . ' line ' . $e->getLine() . "\n";

        if ($e instanceof self) {
            if ($e->shortMessage) {
                $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->shortMessage) . "\n";
            }
            foreach ($e as $ee) {
                $message .= "\n" . $this->getExceptionMessage($ee, $depth + 1);
            }
        }  else {
            $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getMessage()) . "\n";
            $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getTraceAsString()) . "\n";
        }

        return str_replace(getcwd(), '.', $message);
    }
}
<?php

namespace Guzzle\Common\Exception;

/**
 * Guzzle exception
 */
interface GuzzleException {}
<?php

namespace Guzzle\Common\Exception;

class InvalidArgumentException extends \InvalidArgumentException implements GuzzleException {}
<?php

namespace Guzzle\Common\Exception;

class RuntimeException extends \RuntimeException implements GuzzleException {}
<?php

namespace Guzzle\Common\Exception;

class UnexpectedValueException extends \UnexpectedValueException implements GuzzleException {}
<?php

namespace Guzzle\Common;

/**
 * Interfaces that adds a factory method which is used to instantiate a class from an array of configuration options.
 */
interface FromConfigInterface
{
    /**
     * Static factory method used to turn an array or collection of configuration data into an instantiated object.
     *
     * @param array|Collection $config Configuration data
     *
     * @return FromConfigInterface
     */
    public static function factory($config = array());
}
<?php

namespace Guzzle\Common;

use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Holds an event dispatcher
 */
interface HasDispatcherInterface
{
    /**
     * Get a list of all of the events emitted from the class
     *
     * @return array
     */
    public static function getAllEvents();

    /**
     * Set the EventDispatcher of the request
     *
     * @param EventDispatcherInterface $eventDispatcher
     *
     * @return self
     */
    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher);

    /**
     * Get the EventDispatcher of the request
     *
     * @return EventDispatcherInterface
     */
    public function getEventDispatcher();

    /**
     * Helper to dispatch Guzzle events and set the event name on the event
     *
     * @param string $eventName Name of the event to dispatch
     * @param array  $context   Context of the event
     *
     * @return Event Returns the created event object
     */
    public function dispatch($eventName, array $context = array());

    /**
     * Add an event subscriber to the dispatcher
     *
     * @param EventSubscriberInterface $subscriber Event subscriber
     *
     * @return self
     */
    public function addSubscriber(EventSubscriberInterface $subscriber);
}
<?php

namespace Guzzle\Common;

/**
 * An object that can be represented as an array
 */
interface ToArrayInterface
{
    /**
     * Get the array representation of an object
     *
     * @return array
     */
    public function toArray();
}
<?php

namespace Guzzle\Common;

/**
 * Guzzle version information
 */
class Version
{
    const VERSION = '3.9.3';

    /**
     * @var bool Set this value to true to enable warnings for deprecated functionality use. This should be on in your
     *           unit tests, but probably not in production.
     */
    public static $emitWarnings = false;

    /**
     * Emit a deprecation warning
     *
     * @param string $message Warning message
     */
    public static function warn($message)
    {
        if (self::$emitWarnings) {
            trigger_error('Deprecation warning: ' . $message, E_USER_DEPRECATED);
        }
    }
}
<?php

namespace Guzzle\Http;

use Guzzle\Stream\Stream;

/**
 * Abstract decorator used to wrap entity bodies
 */
class AbstractEntityBodyDecorator implements EntityBodyInterface
{
    /** @var EntityBodyInterface Decorated entity body */
    protected $body;

    /**
     * @param EntityBodyInterface $body Entity body to decorate
     */
    public function __construct(EntityBodyInterface $body)
    {
        $this->body = $body;
    }

    public function __toString()
    {
        return (string) $this->body;
    }

    /**
     * Allow decorators to implement custom methods
     *
     * @param string $method Missing method name
     * @param array  $args   Method arguments
     *
     * @return mixed
     */
    public function __call($method, array $args)
    {
        return call_user_func_array(array($this->body, $method), $args);
    }

    public function close()
    {
        return $this->body->close();
    }

    public function setRewindFunction($callable)
    {
        $this->body->setRewindFunction($callable);

        return $this;
    }

    public function rewind()
    {
        return $this->body->rewind();
    }

    public function compress($filter = 'zlib.deflate')
    {
        return $this->body->compress($filter);
    }

    public function uncompress($filter = 'zlib.inflate')
    {
        return $this->body->uncompress($filter);
    }

    public function getContentLength()
    {
        return $this->getSize();
    }

    public function getContentType()
    {
        return $this->body->getContentType();
    }

    public function getContentMd5($rawOutput = false, $base64Encode = false)
    {
        $hash = Stream::getHash($this, 'md5', $rawOutput);

        return $hash && $base64Encode ? base64_encode($hash) : $hash;
    }

    public function getContentEncoding()
    {
        return $this->body->getContentEncoding();
    }

    public function getMetaData($key = null)
    {
        return $this->body->getMetaData($key);
    }

    public function getStream()
    {
        return $this->body->getStream();
    }

    public function setStream($stream, $size = 0)
    {
        $this->body->setStream($stream, $size);

        return $this;
    }

    public function detachStream()
    {
        $this->body->detachStream();

        return $this;
    }

    public function getWrapper()
    {
        return $this->body->getWrapper();
    }

    public function getWrapperData()
    {
        return $this->body->getWrapperData();
    }

    public function getStreamType()
    {
        return $this->body->getStreamType();
    }

    public function getUri()
    {
        return $this->body->getUri();
    }

    public function getSize()
    {
        return $this->body->getSize();
    }

    public function isReadable()
    {
        return $this->body->isReadable();
    }

    public function isRepeatable()
    {
        return $this->isSeekable() && $this->isReadable();
    }

    public function isWritable()
    {
        return $this->body->isWritable();
    }

    public function isConsumed()
    {
        return $this->body->isConsumed();
    }

    /**
     * Alias of isConsumed()
     * {@inheritdoc}
     */
    public function feof()
    {
        return $this->isConsumed();
    }

    public function isLocal()
    {
        return $this->body->isLocal();
    }

    public function isSeekable()
    {
        return $this->body->isSeekable();
    }

    public function setSize($size)
    {
        $this->body->setSize($size);

        return $this;
    }

    public function seek($offset, $whence = SEEK_SET)
    {
        return $this->body->seek($offset, $whence);
    }

    public function read($length)
    {
        return $this->body->read($length);
    }

    public function write($string)
    {
        return $this->body->write($string);
    }

    public function readLine($maxLength = null)
    {
        return $this->body->readLine($maxLength);
    }

    public function ftell()
    {
        return $this->body->ftell();
    }

    public function getCustomData($key)
    {
        return $this->body->getCustomData($key);
    }

    public function setCustomData($key, $value)
    {
        $this->body->setCustomData($key, $value);

        return $this;
    }
}
<?php

namespace Guzzle\Http;

use Guzzle\Common\Exception\RuntimeException;

/**
 * EntityBody decorator that can cache previously read bytes from a sequentially read tstream
 */
class CachingEntityBody extends AbstractEntityBodyDecorator
{
    /** @var EntityBody Remote stream used to actually pull data onto the buffer */
    protected $remoteStream;

    /** @var int The number of bytes to skip reading due to a write on the temporary buffer */
    protected $skipReadBytes = 0;

    /**
     * We will treat the buffer object as the body of the entity body
     * {@inheritdoc}
     */
    public function __construct(EntityBodyInterface $body)
    {
        $this->remoteStream = $body;
        $this->body = new EntityBody(fopen('php://temp', 'r+'));
    }

    /**
     * Will give the contents of the buffer followed by the exhausted remote stream.
     *
     * Warning: Loads the entire stream into memory
     *
     * @return string
     */
    public function __toString()
    {
        $pos = $this->ftell();
        $this->rewind();

        $str = '';
        while (!$this->isConsumed()) {
            $str .= $this->read(16384);
        }

        $this->seek($pos);

        return $str;
    }

    public function getSize()
    {
        return max($this->body->getSize(), $this->remoteStream->getSize());
    }

    /**
     * {@inheritdoc}
     * @throws RuntimeException When seeking with SEEK_END or when seeking past the total size of the buffer stream
     */
    public function seek($offset, $whence = SEEK_SET)
    {
        if ($whence == SEEK_SET) {
            $byte = $offset;
        } elseif ($whence == SEEK_CUR) {
            $byte = $offset + $this->ftell();
        } else {
            throw new RuntimeException(__CLASS__ . ' supports only SEEK_SET and SEEK_CUR seek operations');
        }

        // You cannot skip ahead past where you've read from the remote stream
        if ($byte > $this->body->getSize()) {
            throw new RuntimeException(
                "Cannot seek to byte {$byte} when the buffered stream only contains {$this->body->getSize()} bytes"
            );
        }

        return $this->body->seek($byte);
    }

    public function rewind()
    {
        return $this->seek(0);
    }

    /**
     * Does not support custom rewind functions
     *
     * @throws RuntimeException
     */
    public function setRewindFunction($callable)
    {
        throw new RuntimeException(__CLASS__ . ' does not support custom stream rewind functions');
    }

    public function read($length)
    {
        // Perform a regular read on any previously read data from the buffer
        $data = $this->body->read($length);
        $remaining = $length - strlen($data);

        // More data was requested so read from the remote stream
        if ($remaining) {
            // If data was written to the buffer in a position that would have been filled from the remote stream,
            // then we must skip bytes on the remote stream to emulate overwriting bytes from that position. This
            // mimics the behavior of other PHP stream wrappers.
            $remoteData = $this->remoteStream->read($remaining + $this->skipReadBytes);

            if ($this->skipReadBytes) {
                $len = strlen($remoteData);
                $remoteData = substr($remoteData, $this->skipReadBytes);
                $this->skipReadBytes = max(0, $this->skipReadBytes - $len);
            }

            $data .= $remoteData;
            $this->body->write($remoteData);
        }

        return $data;
    }

    public function write($string)
    {
        // When appending to the end of the currently read stream, you'll want to skip bytes from being read from
        // the remote stream to emulate other stream wrappers. Basically replacing bytes of data of a fixed length.
        $overflow = (strlen($string) + $this->ftell()) - $this->remoteStream->ftell();
        if ($overflow > 0) {
            $this->skipReadBytes += $overflow;
        }

        return $this->body->write($string);
    }

    /**
     * {@inheritdoc}
     * @link http://php.net/manual/en/function.fgets.php
     */
    public function readLine($maxLength = null)
    {
        $buffer = '';
        $size = 0;
        while (!$this->isConsumed()) {
            $byte = $this->read(1);
            $buffer .= $byte;
            // Break when a new line is found or the max length - 1 is reached
            if ($byte == PHP_EOL || ++$size == $maxLength - 1) {
                break;
            }
        }

        return $buffer;
    }

    public function isConsumed()
    {
        return $this->body->isConsumed() && $this->remoteStream->isConsumed();
    }

    /**
     * Close both the remote stream and buffer stream
     */
    public function close()
    {
        return $this->remoteStream->close() && $this->body->close();
    }

    public function setStream($stream, $size = 0)
    {
        $this->remoteStream->setStream($stream, $size);
    }

    public function getContentType()
    {
        return $this->remoteStream->getContentType();
    }

    public function getContentEncoding()
    {
        return $this->remoteStream->getContentEncoding();
    }

    public function getMetaData($key = null)
    {
        return $this->remoteStream->getMetaData($key);
    }

    public function getStream()
    {
        return $this->remoteStream->getStream();
    }

    public function getWrapper()
    {
        return $this->remoteStream->getWrapper();
    }

    public function getWrapperData()
    {
        return $this->remoteStream->getWrapperData();
    }

    public function getStreamType()
    {
        return $this->remoteStream->getStreamType();
    }

    public function getUri()
    {
        return $this->remoteStream->getUri();
    }

    /**
     * Always retrieve custom data from the remote stream
     * {@inheritdoc}
     */
    public function getCustomData($key)
    {
        return $this->remoteStream->getCustomData($key);
    }

    /**
     * Always set custom data on the remote stream
     * {@inheritdoc}
     */
    public function setCustomData($key, $value)
    {
        $this->remoteStream->setCustomData($key, $value);

        return $this;
    }
}
<?php

namespace Guzzle\Http;

use Guzzle\Common\Collection;
use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Common\Exception\ExceptionCollection;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Common\Version;
use Guzzle\Parser\ParserRegistry;
use Guzzle\Parser\UriTemplate\UriTemplateInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\RequestFactory;
use Guzzle\Http\Message\RequestFactoryInterface;
use Guzzle\Http\Curl\CurlMultiInterface;
use Guzzle\Http\Curl\CurlMultiProxy;
use Guzzle\Http\Curl\CurlHandle;
use Guzzle\Http\Curl\CurlVersion;

/**
 * HTTP client
 */
class Client extends AbstractHasDispatcher implements ClientInterface
{
    /** @deprecated Use [request.options][params] */
    const REQUEST_PARAMS = 'request.params';

    const REQUEST_OPTIONS = 'request.options';
    const CURL_OPTIONS = 'curl.options';
    const SSL_CERT_AUTHORITY = 'ssl.certificate_authority';
    const DISABLE_REDIRECTS = RedirectPlugin::DISABLE;
    const DEFAULT_SELECT_TIMEOUT = 1.0;
    const MAX_HANDLES = 3;

    /** @var Collection Default HTTP headers to set on each request */
    protected $defaultHeaders;

    /** @var string The user agent string to set on each request */
    protected $userAgent;

    /** @var Collection Parameter object holding configuration data */
    private $config;

    /** @var Url Base URL of the client */
    private $baseUrl;

    /** @var CurlMultiInterface CurlMulti object used internally */
    private $curlMulti;

    /** @var UriTemplateInterface URI template owned by the client */
    private $uriTemplate;

    /** @var RequestFactoryInterface Request factory used by the client */
    protected $requestFactory;

    public static function getAllEvents()
    {
        return array(self::CREATE_REQUEST);
    }

    /**
     * @param string           $baseUrl Base URL of the web service
     * @param array|Collection $config  Configuration settings
     *
     * @throws RuntimeException if cURL is not installed
     */
    public function __construct($baseUrl = '', $config = null)
    {
        if (!extension_loaded('curl')) {
            // @codeCoverageIgnoreStart
            throw new RuntimeException('The PHP cURL extension must be installed to use Guzzle.');
            // @codeCoverageIgnoreEnd
        }
        $this->setConfig($config ?: new Collection());
        $this->initSsl();
        $this->setBaseUrl($baseUrl);
        $this->defaultHeaders = new Collection();
        $this->setRequestFactory(RequestFactory::getInstance());
        $this->userAgent = $this->getDefaultUserAgent();
        if (!$this->config[self::DISABLE_REDIRECTS]) {
            $this->addSubscriber(new RedirectPlugin());
        }
    }

    final public function setConfig($config)
    {
        if ($config instanceof Collection) {
            $this->config = $config;
        } elseif (is_array($config)) {
            $this->config = new Collection($config);
        } else {
            throw new InvalidArgumentException('Config must be an array or Collection');
        }

        return $this;
    }

    final public function getConfig($key = false)
    {
        return $key ? $this->config[$key] : $this->config;
    }

    /**
     * Set a default request option on the client that will be used as a default for each request
     *
     * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo)
     * @param mixed  $value     Value to set
     *
     * @return $this
     */
    public function setDefaultOption($keyOrPath, $value)
    {
        $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath;
        $this->config->setPath($keyOrPath, $value);

        return $this;
    }

    /**
     * Retrieve a default request option from the client
     *
     * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo)
     *
     * @return mixed|null
     */
    public function getDefaultOption($keyOrPath)
    {
        $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath;

        return $this->config->getPath($keyOrPath);
    }

    final public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2)
    {
        $opts = $this->config[self::CURL_OPTIONS] ?: array();

        if ($certificateAuthority === true) {
            // use bundled CA bundle, set secure defaults
            $opts[CURLOPT_CAINFO] = __DIR__ . '/Resources/cacert.pem';
            $opts[CURLOPT_SSL_VERIFYPEER] = true;
            $opts[CURLOPT_SSL_VERIFYHOST] = 2;
        } elseif ($certificateAuthority === false) {
            unset($opts[CURLOPT_CAINFO]);
            $opts[CURLOPT_SSL_VERIFYPEER] = false;
            $opts[CURLOPT_SSL_VERIFYHOST] = 0;
        } elseif ($verifyPeer !== true && $verifyPeer !== false && $verifyPeer !== 1 && $verifyPeer !== 0) {
            throw new InvalidArgumentException('verifyPeer must be 1, 0 or boolean');
        } elseif ($verifyHost !== 0 && $verifyHost !== 1 && $verifyHost !== 2) {
            throw new InvalidArgumentException('verifyHost must be 0, 1 or 2');
        } else {
            $opts[CURLOPT_SSL_VERIFYPEER] = $verifyPeer;
            $opts[CURLOPT_SSL_VERIFYHOST] = $verifyHost;
            if (is_file($certificateAuthority)) {
                unset($opts[CURLOPT_CAPATH]);
                $opts[CURLOPT_CAINFO] = $certificateAuthority;
            } elseif (is_dir($certificateAuthority)) {
                unset($opts[CURLOPT_CAINFO]);
                $opts[CURLOPT_CAPATH] = $certificateAuthority;
            } else {
                throw new RuntimeException(
                    'Invalid option passed to ' . self::SSL_CERT_AUTHORITY . ': ' . $certificateAuthority
                );
            }
        }

        $this->config->set(self::CURL_OPTIONS, $opts);

        return $this;
    }

    public function createRequest($method = 'GET', $uri = null, $headers = null, $body = null, array $options = array())
    {
        if (!$uri) {
            $url = $this->getBaseUrl();
        } else {
            if (!is_array($uri)) {
                $templateVars = null;
            } else {
                list($uri, $templateVars) = $uri;
            }
            if (strpos($uri, '://')) {
                // Use absolute URLs as-is
                $url = $this->expandTemplate($uri, $templateVars);
            } else {
                $url = Url::factory($this->getBaseUrl())->combine($this->expandTemplate($uri, $templateVars));
            }
        }

        // If default headers are provided, then merge them under any explicitly provided headers for the request
        if (count($this->defaultHeaders)) {
            if (!$headers) {
                $headers = $this->defaultHeaders->toArray();
            } elseif (is_array($headers)) {
                $headers += $this->defaultHeaders->toArray();
            } elseif ($headers instanceof Collection) {
                $headers = $headers->toArray() + $this->defaultHeaders->toArray();
            }
        }

        return $this->prepareRequest($this->requestFactory->create($method, (string) $url, $headers, $body), $options);
    }

    public function getBaseUrl($expand = true)
    {
        return $expand ? $this->expandTemplate($this->baseUrl) : $this->baseUrl;
    }

    public function setBaseUrl($url)
    {
        $this->baseUrl = $url;

        return $this;
    }

    public function setUserAgent($userAgent, $includeDefault = false)
    {
        if ($includeDefault) {
            $userAgent .= ' ' . $this->getDefaultUserAgent();
        }
        $this->userAgent = $userAgent;

        return $this;
    }

    /**
     * Get the default User-Agent string to use with Guzzle
     *
     * @return string
     */
    public function getDefaultUserAgent()
    {
        return 'Guzzle/' . Version::VERSION
            . ' curl/' . CurlVersion::getInstance()->get('version')
            . ' PHP/' . PHP_VERSION;
    }

    public function get($uri = null, $headers = null, $options = array())
    {
        // BC compat: $options can be a string, resource, etc to specify where the response body is downloaded
        return is_array($options)
            ? $this->createRequest('GET', $uri, $headers, null, $options)
            : $this->createRequest('GET', $uri, $headers, $options);
    }

    public function head($uri = null, $headers = null, array $options = array())
    {
        return $this->createRequest('HEAD', $uri, $headers, null, $options);
    }

    public function delete($uri = null, $headers = null, $body = null, array $options = array())
    {
        return $this->createRequest('DELETE', $uri, $headers, $body, $options);
    }

    public function put($uri = null, $headers = null, $body = null, array $options = array())
    {
        return $this->createRequest('PUT', $uri, $headers, $body, $options);
    }

    public function patch($uri = null, $headers = null, $body = null, array $options = array())
    {
        return $this->createRequest('PATCH', $uri, $headers, $body, $options);
    }

    public function post($uri = null, $headers = null, $postBody = null, array $options = array())
    {
        return $this->createRequest('POST', $uri, $headers, $postBody, $options);
    }

    public function options($uri = null, array $options = array())
    {
        return $this->createRequest('OPTIONS', $uri, $options);
    }

    public function send($requests)
    {
        if (!($requests instanceof RequestInterface)) {
            return $this->sendMultiple($requests);
        }

        try {
            /** @var $requests RequestInterface  */
            $this->getCurlMulti()->add($requests)->send();
            return $requests->getResponse();
        } catch (ExceptionCollection $e) {
            throw $e->getFirst();
        }
    }

    /**
     * Set a curl multi object to be used internally by the client for transferring requests.
     *
     * @param CurlMultiInterface $curlMulti Multi object
     *
     * @return self
     */
    public function setCurlMulti(CurlMultiInterface $curlMulti)
    {
        $this->curlMulti = $curlMulti;

        return $this;
    }

    /**
     * @return CurlMultiInterface|CurlMultiProxy
     */
    public function getCurlMulti()
    {
        if (!$this->curlMulti) {
            $this->curlMulti = new CurlMultiProxy(
                self::MAX_HANDLES,
                $this->getConfig('select_timeout') ?: self::DEFAULT_SELECT_TIMEOUT
            );
        }

        return $this->curlMulti;
    }

    public function setRequestFactory(RequestFactoryInterface $factory)
    {
        $this->requestFactory = $factory;

        return $this;
    }

    /**
     * Set the URI template expander to use with the client
     *
     * @param UriTemplateInterface $uriTemplate URI template expander
     *
     * @return self
     */
    public function setUriTemplate(UriTemplateInterface $uriTemplate)
    {
        $this->uriTemplate = $uriTemplate;

        return $this;
    }

    /**
     * Expand a URI template while merging client config settings into the template variables
     *
     * @param string $template  Template to expand
     * @param array  $variables Variables to inject
     *
     * @return string
     */
    protected function expandTemplate($template, array $variables = null)
    {
        $expansionVars = $this->getConfig()->toArray();
        if ($variables) {
            $expansionVars = $variables + $expansionVars;
        }

        return $this->getUriTemplate()->expand($template, $expansionVars);
    }

    /**
     * Get the URI template expander used by the client
     *
     * @return UriTemplateInterface
     */
    protected function getUriTemplate()
    {
        if (!$this->uriTemplate) {
            $this->uriTemplate = ParserRegistry::getInstance()->getParser('uri_template');
        }

        return $this->uriTemplate;
    }

    /**
     * Send multiple requests in parallel
     *
     * @param array $requests Array of RequestInterface objects
     *
     * @return array Returns an array of Response objects
     */
    protected function sendMultiple(array $requests)
    {
        $curlMulti = $this->getCurlMulti();
        foreach ($requests as $request) {
            $curlMulti->add($request);
        }
        $curlMulti->send();

        /** @var $request RequestInterface */
        $result = array();
        foreach ($requests as $request) {
            $result[] = $request->getResponse();
        }

        return $result;
    }

    /**
     * Prepare a request to be sent from the Client by adding client specific behaviors and properties to the request.
     *
     * @param RequestInterface $request Request to prepare for the client
     * @param array            $options Options to apply to the request
     *
     * @return RequestInterface
     */
    protected function prepareRequest(RequestInterface $request, array $options = array())
    {
        $request->setClient($this)->setEventDispatcher(clone $this->getEventDispatcher());

        if ($curl = $this->config[self::CURL_OPTIONS]) {
            $request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($curl));
        }

        if ($params = $this->config[self::REQUEST_PARAMS]) {
            Version::warn('request.params is deprecated. Use request.options to add default request options.');
            $request->getParams()->overwriteWith($params);
        }

        if ($this->userAgent && !$request->hasHeader('User-Agent')) {
            $request->setHeader('User-Agent', $this->userAgent);
        }

        if ($defaults = $this->config[self::REQUEST_OPTIONS]) {
            $this->requestFactory->applyOptions($request, $defaults, RequestFactoryInterface::OPTIONS_AS_DEFAULTS);
        }

        if ($options) {
            $this->requestFactory->applyOptions($request, $options);
        }

        $this->dispatch('client.create_request', array('client' => $this, 'request' => $request));

        return $request;
    }

    /**
     * Initializes SSL settings
     */
    protected function initSsl()
    {
        $authority = $this->config[self::SSL_CERT_AUTHORITY];

        if ($authority === 'system') {
            return;
        }

        if ($authority === null) {
            $authority = true;
        }

        if ($authority === true && substr(__FILE__, 0, 7) == 'phar://') {
            $authority = self::extractPharCacert(__DIR__ . '/Resources/cacert.pem');
        }

        $this->setSslVerification($authority);
    }

    /**
     * @deprecated
     */
    public function getDefaultHeaders()
    {
        Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to retrieve default request options');
        return $this->defaultHeaders;
    }

    /**
     * @deprecated
     */
    public function setDefaultHeaders($headers)
    {
        Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to specify default request options');
        if ($headers instanceof Collection) {
            $this->defaultHeaders = $headers;
        } elseif (is_array($headers)) {
            $this->defaultHeaders = new Collection($headers);
        } else {
            throw new InvalidArgumentException('Headers must be an array or Collection');
        }

        return $this;
    }

    /**
     * @deprecated
     */
    public function preparePharCacert($md5Check = true)
    {
        return sys_get_temp_dir() . '/guzzle-cacert.pem';
    }

    /**
     * Copies the phar cacert from a phar into the temp directory.
     *
     * @param string $pharCacertPath Path to the phar cacert. For example:
     *                               'phar://aws.phar/Guzzle/Http/Resources/cacert.pem'
     *
     * @return string Returns the path to the extracted cacert file.
     * @throws \RuntimeException Throws if the phar cacert cannot be found or
     *                           the file cannot be copied to the temp dir.
     */
    public static function extractPharCacert($pharCacertPath)
    {
        // Copy the cacert.pem file from the phar if it is not in the temp
        // folder.
        $certFile = sys_get_temp_dir() . '/guzzle-cacert.pem';

        if (!file_exists($pharCacertPath)) {
            throw new \RuntimeException("Could not find $pharCacertPath");
        }

        if (!file_exists($certFile) ||
            filesize($certFile) != filesize($pharCacertPath)
        ) {
            if (!copy($pharCacertPath, $certFile)) {
                throw new \RuntimeException(
                    "Could not copy {$pharCacertPath} to {$certFile}: "
                    . var_export(error_get_last(), true)
                );
            }
        }

        return $certFile;
    }
}
<?php

namespace Guzzle\Http;

use Guzzle\Common\HasDispatcherInterface;
use Guzzle\Common\Collection;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\RequestInterface;

/**
 * Client interface for send HTTP requests
 */
interface ClientInterface extends HasDispatcherInterface
{
    const CREATE_REQUEST = 'client.create_request';

    /** @var string RFC 1123 HTTP-Date */
    const HTTP_DATE = 'D, d M Y H:i:s \G\M\T';

    /**
     * Set the configuration object to use with the client
     *
     * @param array|Collection $config Parameters that define how the client behaves
     *
     * @return self
     */
    public function setConfig($config);

    /**
     * Get a configuration setting or all of the configuration settings. The Collection result of this method can be
     * modified to change the configuration settings of a client.
     *
     * A client should honor the following special values:
     *
     * - request.options: Associative array of default RequestFactory options to apply to each request
     * - request.params: Associative array of request parameters (data values) to apply to each request
     * - curl.options: Associative array of cURL configuration settings to apply to each request
     * - ssl.certificate_authority: Path a CAINFO, CAPATH, true to use strict defaults, or false to disable verification
     * - redirect.disable: Set to true to disable redirects
     *
     * @param bool|string $key Configuration value to retrieve. Set to FALSE to retrieve all values of the client.
     *                         The object return can be modified, and modifications will affect the client's config.
     * @return mixed|Collection
     * @see \Guzzle\Http\Message\RequestFactoryInterface::applyOptions for a full list of request.options options
     */
    public function getConfig($key = false);

    /**
     * Create and return a new {@see RequestInterface} configured for the client.
     *
     * Use an absolute path to override the base path of the client, or a relative path to append to the base path of
     * the client. The URI can contain the query string as well. Use an array to provide a URI template and additional
     * variables to use in the URI template expansion.
     *
     * @param string                                    $method  HTTP method. Defaults to GET
     * @param string|array                              $uri     Resource URI.
     * @param array|Collection                          $headers HTTP headers
     * @param string|resource|array|EntityBodyInterface $body    Entity body of request (POST/PUT) or response (GET)
     * @param array                                     $options Array of options to apply to the request
     *
     * @return RequestInterface
     * @throws InvalidArgumentException if a URI array is passed that does not contain exactly two elements: the URI
     *                                  followed by template variables
     */
    public function createRequest(
        $method = RequestInterface::GET,
        $uri = null,
        $headers = null,
        $body = null,
        array $options = array()
    );

    /**
     * Create a GET request for the client
     *
     * @param string|array     $uri     Resource URI
     * @param array|Collection $headers HTTP headers
     * @param array            $options Options to apply to the request. For BC compatibility, you can also pass a
     *                                  string to tell Guzzle to download the body of the response to a particular
     *                                  location. Use the 'body' option instead for forward compatibility.
     * @return RequestInterface
     * @see    Guzzle\Http\ClientInterface::createRequest()
     */
    public function get($uri = null, $headers = null, $options = array());

    /**
     * Create a HEAD request for the client
     *
     * @param string|array     $uri     Resource URI
     * @param array|Collection $headers HTTP headers
     * @param array            $options Options to apply to the request
     *
     * @return RequestInterface
     * @see    Guzzle\Http\ClientInterface::createRequest()
     */
    public function head($uri = null, $headers = null, array $options = array());

    /**
     * Create a DELETE request for the client
     *
     * @param string|array                        $uri     Resource URI
     * @param array|Collection                    $headers HTTP headers
     * @param string|resource|EntityBodyInterface $body    Body to send in the request
     * @param array                               $options Options to apply to the request
     *
     * @return EntityEnclosingRequestInterface
     * @see    Guzzle\Http\ClientInterface::createRequest()
     */
    public function delete($uri = null, $headers = null, $body = null, array $options = array());

    /**
     * Create a PUT request for the client
     *
     * @param string|array                        $uri     Resource URI
     * @param array|Collection                    $headers HTTP headers
     * @param string|resource|EntityBodyInterface $body    Body to send in the request
     * @param array                               $options Options to apply to the request
     *
     * @return EntityEnclosingRequestInterface
     * @see    Guzzle\Http\ClientInterface::createRequest()
     */
    public function put($uri = null, $headers = null, $body = null, array $options = array());

    /**
     * Create a PATCH request for the client
     *
     * @param string|array                        $uri     Resource URI
     * @param array|Collection                    $headers HTTP headers
     * @param string|resource|EntityBodyInterface $body    Body to send in the request
     * @param array                               $options Options to apply to the request
     *
     * @return EntityEnclosingRequestInterface
     * @see    Guzzle\Http\ClientInterface::createRequest()
     */
    public function patch($uri = null, $headers = null, $body = null, array $options = array());

    /**
     * Create a POST request for the client
     *
     * @param string|array                                $uri      Resource URI
     * @param array|Collection                            $headers  HTTP headers
     * @param array|Collection|string|EntityBodyInterface $postBody POST body. Can be a string, EntityBody, or
     *                                                    associative array of POST fields to send in the body of the
     *                                                    request. Prefix a value in the array with the @ symbol to
     *                                                    reference a file.
     * @param array                                       $options Options to apply to the request
     *
     * @return EntityEnclosingRequestInterface
     * @see    Guzzle\Http\ClientInterface::createRequest()
     */
    public function post($uri = null, $headers = null, $postBody = null, array $options = array());

    /**
     * Create an OPTIONS request for the client
     *
     * @param string|array $uri     Resource URI
     * @param array        $options Options to apply to the request
     *
     * @return RequestInterface
     * @see    Guzzle\Http\ClientInterface::createRequest()
     */
    public function options($uri = null, array $options = array());

    /**
     * Sends a single request or an array of requests in parallel
     *
     * @param array|RequestInterface $requests One or more RequestInterface objects to send
     *
     * @return \Guzzle\Http\Message\Response|array Returns a single Response or an array of Response objects
     */
    public function send($requests);

    /**
     * Get the client's base URL as either an expanded or raw URI template
     *
     * @param bool $expand Set to FALSE to get the raw base URL without URI template expansion
     *
     * @return string|null
     */
    public function getBaseUrl($expand = true);

    /**
     * Set the base URL of the client
     *
     * @param string $url The base service endpoint URL of the webservice
     *
     * @return self
     */
    public function setBaseUrl($url);

    /**
     * Set the User-Agent header to be used on all requests from the client
     *
     * @param string $userAgent      User agent string
     * @param bool   $includeDefault Set to true to prepend the value to Guzzle's default user agent string
     *
     * @return self
     */
    public function setUserAgent($userAgent, $includeDefault = false);

    /**
     * Set SSL verification options.
     *
     * Setting $certificateAuthority to TRUE will result in the bundled cacert.pem being used to verify against the
     * remote host.
     *
     * Alternate certificates to verify against can be specified with the $certificateAuthority option set to the full
     * path to a certificate file, or the path to a directory containing certificates.
     *
     * Setting $certificateAuthority to FALSE will turn off peer verification, unset the bundled cacert.pem, and
     * disable host verification. Please don't do this unless you really know what you're doing, and why you're doing
     * it.
     *
     * @param string|bool $certificateAuthority bool, file path, or directory path
     * @param bool        $verifyPeer           FALSE to stop from verifying the peer's certificate.
     * @param int         $verifyHost           Set to 1 to check the existence of a common name in the SSL peer
     *                                          certificate. 2 to check the existence of a common name and also verify
     *                                          that it matches the hostname provided.
     * @return self
     */
    public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2);
}
<?php

namespace Guzzle\Http\Curl;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Common\Collection;
use Guzzle\Http\Message\EntityEnclosingRequest;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Parser\ParserRegistry;
use Guzzle\Http\Url;

/**
 * Immutable wrapper for a cURL handle
 */
class CurlHandle
{
    const BODY_AS_STRING = 'body_as_string';
    const PROGRESS = 'progress';
    const DEBUG = 'debug';

    /** @var Collection Curl options */
    protected $options;

    /** @var resource Curl resource handle */
    protected $handle;

    /** @var int CURLE_* error */
    protected $errorNo = CURLE_OK;

    /**
     * Factory method to create a new curl handle based on an HTTP request.
     *
     * There are some helpful options you can set to enable specific behavior:
     * - debug:    Set to true to enable cURL debug functionality to track the actual headers sent over the wire.
     * - progress: Set to true to enable progress function callbacks.
     *
     * @param RequestInterface $request Request
     *
     * @return CurlHandle
     * @throws RuntimeException
     */
    public static function factory(RequestInterface $request)
    {
        $requestCurlOptions = $request->getCurlOptions();
        $mediator = new RequestMediator($request, $requestCurlOptions->get('emit_io'));
        $tempContentLength = null;
        $method = $request->getMethod();
        $bodyAsString = $requestCurlOptions->get(self::BODY_AS_STRING);

        // Prepare url
        $url = (string)$request->getUrl();
        if(($pos = strpos($url, '#')) !== false ){
            // strip fragment from url
            $url = substr($url, 0, $pos);
        }

        // Array of default cURL options.
        $curlOptions = array(
            CURLOPT_URL            => $url,
            CURLOPT_CONNECTTIMEOUT => 150,
            CURLOPT_RETURNTRANSFER => false,
            CURLOPT_HEADER         => false,
            CURLOPT_PORT           => $request->getPort(),
            CURLOPT_HTTPHEADER     => array(),
            CURLOPT_WRITEFUNCTION  => array($mediator, 'writeResponseBody'),
            CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader'),
            CURLOPT_HTTP_VERSION   => $request->getProtocolVersion() === '1.0'
                ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1,
            // Verifies the authenticity of the peer's certificate
            CURLOPT_SSL_VERIFYPEER => 1,
            // Certificate must indicate that the server is the server to which you meant to connect
            CURLOPT_SSL_VERIFYHOST => 2
        );

        if (defined('CURLOPT_PROTOCOLS')) {
            // Allow only HTTP and HTTPS protocols
            $curlOptions[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
        }

        // Add CURLOPT_ENCODING if Accept-Encoding header is provided
        if ($acceptEncodingHeader = $request->getHeader('Accept-Encoding')) {
            $curlOptions[CURLOPT_ENCODING] = (string) $acceptEncodingHeader;
            // Let cURL set the Accept-Encoding header, prevents duplicate values
            $request->removeHeader('Accept-Encoding');
        }

        // Enable curl debug information if the 'debug' param was set
        if ($requestCurlOptions->get('debug')) {
            $curlOptions[CURLOPT_STDERR] = fopen('php://temp', 'r+');
            // @codeCoverageIgnoreStart
            if (false === $curlOptions[CURLOPT_STDERR]) {
                throw new RuntimeException('Unable to create a stream for CURLOPT_STDERR');
            }
            // @codeCoverageIgnoreEnd
            $curlOptions[CURLOPT_VERBOSE] = true;
        }

        // Specify settings according to the HTTP method
        if ($method == 'GET') {
            $curlOptions[CURLOPT_HTTPGET] = true;
        } elseif ($method == 'HEAD') {
            $curlOptions[CURLOPT_NOBODY] = true;
            // HEAD requests do not use a write function
            unset($curlOptions[CURLOPT_WRITEFUNCTION]);
        } elseif (!($request instanceof EntityEnclosingRequest)) {
            $curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
        } else {

            $curlOptions[CURLOPT_CUSTOMREQUEST] = $method;

            // Handle sending raw bodies in a request
            if ($request->getBody()) {
                // You can send the body as a string using curl's CURLOPT_POSTFIELDS
                if ($bodyAsString) {
                    $curlOptions[CURLOPT_POSTFIELDS] = (string) $request->getBody();
                    // Allow curl to add the Content-Length for us to account for the times when
                    // POST redirects are followed by GET requests
                    if ($tempContentLength = $request->getHeader('Content-Length')) {
                        $tempContentLength = (int) (string) $tempContentLength;
                    }
                    // Remove the curl generated Content-Type header if none was set manually
                    if (!$request->hasHeader('Content-Type')) {
                        $curlOptions[CURLOPT_HTTPHEADER][] = 'Content-Type:';
                    }
                } else {
                    $curlOptions[CURLOPT_UPLOAD] = true;
                    // Let cURL handle setting the Content-Length header
                    if ($tempContentLength = $request->getHeader('Content-Length')) {
                        $tempContentLength = (int) (string) $tempContentLength;
                        $curlOptions[CURLOPT_INFILESIZE] = $tempContentLength;
                    }
                    // Add a callback for curl to read data to send with the request only if a body was specified
                    $curlOptions[CURLOPT_READFUNCTION] = array($mediator, 'readRequestBody');
                    // Attempt to seek to the start of the stream
                    $request->getBody()->seek(0);
                }

            } else {

                // Special handling for POST specific fields and files
                $postFields = false;
                if (count($request->getPostFiles())) {
                    $postFields = $request->getPostFields()->useUrlEncoding(false)->urlEncode();
                    foreach ($request->getPostFiles() as $key => $data) {
                        $prefixKeys = count($data) > 1;
                        foreach ($data as $index => $file) {
                            // Allow multiple files in the same key
                            $fieldKey = $prefixKeys ? "{$key}[{$index}]" : $key;
                            $postFields[$fieldKey] = $file->getCurlValue();
                        }
                    }
                } elseif (count($request->getPostFields())) {
                    $postFields = (string) $request->getPostFields()->useUrlEncoding(true);
                }

                if ($postFields !== false) {
                    if ($method == 'POST') {
                        unset($curlOptions[CURLOPT_CUSTOMREQUEST]);
                        $curlOptions[CURLOPT_POST] = true;
                    }
                    $curlOptions[CURLOPT_POSTFIELDS] = $postFields;
                    $request->removeHeader('Content-Length');
                }
            }

            // If the Expect header is not present, prevent curl from adding it
            if (!$request->hasHeader('Expect')) {
                $curlOptions[CURLOPT_HTTPHEADER][] = 'Expect:';
            }
        }

        // If a Content-Length header was specified but we want to allow curl to set one for us
        if (null !== $tempContentLength) {
            $request->removeHeader('Content-Length');
        }

        // Set custom cURL options
        foreach ($requestCurlOptions->toArray() as $key => $value) {
            if (is_numeric($key)) {
                $curlOptions[$key] = $value;
            }
        }

        // Do not set an Accept header by default
        if (!isset($curlOptions[CURLOPT_ENCODING])) {
            $curlOptions[CURLOPT_HTTPHEADER][] = 'Accept:';
        }

        // Add any custom headers to the request. Empty headers will cause curl to not send the header at all.
        foreach ($request->getHeaderLines() as $line) {
            $curlOptions[CURLOPT_HTTPHEADER][] = $line;
        }

        // Add the content-length header back if it was temporarily removed
        if (null !== $tempContentLength) {
            $request->setHeader('Content-Length', $tempContentLength);
        }

        // Apply the options to a new cURL handle.
        $handle = curl_init();

        // Enable the progress function if the 'progress' param was set
        if ($requestCurlOptions->get('progress')) {
            // Wrap the function in a function that provides the curl handle to the mediator's progress function
            // Using this rather than injecting the handle into the mediator prevents a circular reference
            $curlOptions[CURLOPT_PROGRESSFUNCTION] = function () use ($mediator, $handle) {
                $args = func_get_args();
                $args[] = $handle;

                // PHP 5.5 pushed the handle onto the start of the args
                if (is_resource($args[0])) {
                    array_shift($args);
                }

                call_user_func_array(array($mediator, 'progress'), $args);
            };
            $curlOptions[CURLOPT_NOPROGRESS] = false;
        }

        curl_setopt_array($handle, $curlOptions);

        return new static($handle, $curlOptions);
    }

    /**
     * Construct a new CurlHandle object that wraps a cURL handle
     *
     * @param resource         $handle  Configured cURL handle resource
     * @param Collection|array $options Curl options to use with the handle
     *
     * @throws InvalidArgumentException
     */
    public function __construct($handle, $options)
    {
        if (!is_resource($handle)) {
            throw new InvalidArgumentException('Invalid handle provided');
        }
        if (is_array($options)) {
            $this->options = new Collection($options);
        } elseif ($options instanceof Collection) {
            $this->options = $options;
        } else {
            throw new InvalidArgumentException('Expected array or Collection');
        }
        $this->handle = $handle;
    }

    /**
     * Destructor
     */
    public function __destruct()
    {
        $this->close();
    }

    /**
     * Close the curl handle
     */
    public function close()
    {
        if (is_resource($this->handle)) {
            curl_close($this->handle);
        }
        $this->handle = null;
    }

    /**
     * Check if the handle is available and still OK
     *
     * @return bool
     */
    public function isAvailable()
    {
        return is_resource($this->handle);
    }

    /**
     * Get the last error that occurred on the cURL handle
     *
     * @return string
     */
    public function getError()
    {
        return $this->isAvailable() ? curl_error($this->handle) : '';
    }

    /**
     * Get the last error number that occurred on the cURL handle
     *
     * @return int
     */
    public function getErrorNo()
    {
        if ($this->errorNo) {
            return $this->errorNo;
        }

        return $this->isAvailable() ? curl_errno($this->handle) : CURLE_OK;
    }

    /**
     * Set the curl error number
     *
     * @param int $error Error number to set
     *
     * @return CurlHandle
     */
    public function setErrorNo($error)
    {
        $this->errorNo = $error;

        return $this;
    }

    /**
     * Get cURL curl_getinfo data
     *
     * @param int $option Option to retrieve. Pass null to retrieve all data as an array.
     *
     * @return array|mixed
     */
    public function getInfo($option = null)
    {
        if (!is_resource($this->handle)) {
            return null;
        }

        if (null !== $option) {
            return curl_getinfo($this->handle, $option) ?: null;
        }

        return curl_getinfo($this->handle) ?: array();
    }

    /**
     * Get the stderr output
     *
     * @param bool $asResource Set to TRUE to get an fopen resource
     *
     * @return string|resource|null
     */
    public function getStderr($asResource = false)
    {
        $stderr = $this->getOptions()->get(CURLOPT_STDERR);
        if (!$stderr) {
            return null;
        }

        if ($asResource) {
            return $stderr;
        }

        fseek($stderr, 0);
        $e = stream_get_contents($stderr);
        fseek($stderr, 0, SEEK_END);

        return $e;
    }

    /**
     * Get the URL that this handle is connecting to
     *
     * @return Url
     */
    public function getUrl()
    {
        return Url::factory($this->options->get(CURLOPT_URL));
    }

    /**
     * Get the wrapped curl handle
     *
     * @return resource|null Returns the cURL handle or null if it was closed
     */
    public function getHandle()
    {
        return $this->isAvailable() ? $this->handle : null;
    }

    /**
     * Get the cURL setopt options of the handle. Changing values in the return object will have no effect on the curl
     * handle after it is created.
     *
     * @return Collection
     */
    public function getOptions()
    {
        return $this->options;
    }

    /**
     * Update a request based on the log messages of the CurlHandle
     *
     * @param RequestInterface $request Request to update
     */
    public function updateRequestFromTransfer(RequestInterface $request)
    {
        if (!$request->getResponse()) {
            return;
        }

        // Update the transfer stats of the response
        $request->getResponse()->setInfo($this->getInfo());

        if (!$log = $this->getStderr(true)) {
            return;
        }

        // Parse the cURL stderr output for outgoing requests
        $headers = '';
        fseek($log, 0);
        while (($line = fgets($log)) !== false) {
            if ($line && $line[0] == '>') {
                $headers = substr(trim($line), 2) . "\r\n";
                while (($line = fgets($log)) !== false) {
                    if ($line[0] == '*' || $line[0] == '<') {
                        break;
                    } else {
                        $headers .= trim($line) . "\r\n";
                    }
                }
            }
        }

        // Add request headers to the request exactly as they were sent
        if ($headers) {
            $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($headers);
            if (!empty($parsed['headers'])) {
                $request->setHeaders(array());
                foreach ($parsed['headers'] as $name => $value) {
                    $request->setHeader($name, $value);
                }
            }
            if (!empty($parsed['version'])) {
                $request->setProtocolVersion($parsed['version']);
            }
        }
    }

    /**
     * Parse the config and replace curl.* configurators into the constant based values so it can be used elsewhere
     *
     * @param array|Collection $config The configuration we want to parse
     *
     * @return array
     */
    public static function parseCurlConfig($config)
    {
        $curlOptions = array();
        foreach ($config as $key => $value) {
            if (is_string($key) && defined($key)) {
                // Convert constants represented as string to constant int values
                $key = constant($key);
            }
            if (is_string($value) && defined($value)) {
                $value = constant($value);
            }
            $curlOptions[$key] = $value;
        }

        return $curlOptions;
    }
}
<?php

namespace Guzzle\Http\Curl;

use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Common\Event;
use Guzzle\Http\Exception\MultiTransferException;
use Guzzle\Http\Exception\CurlException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Exception\RequestException;

/**
 * Send {@see RequestInterface} objects in parallel using curl_multi
 */
class CurlMulti extends AbstractHasDispatcher implements CurlMultiInterface
{
    /** @var resource cURL multi handle. */
    protected $multiHandle;

    /** @var array Attached {@see RequestInterface} objects. */
    protected $requests;

    /** @var \SplObjectStorage RequestInterface to CurlHandle hash */
    protected $handles;

    /** @var array Hash mapping curl handle resource IDs to request objects */
    protected $resourceHash;

    /** @var array Queued exceptions */
    protected $exceptions = array();

    /** @var array Requests that succeeded */
    protected $successful = array();

    /** @var array cURL multi error values and codes */
    protected $multiErrors = array(
        CURLM_BAD_HANDLE      => array('CURLM_BAD_HANDLE', 'The passed-in handle is not a valid CURLM handle.'),
        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."),
        CURLM_OUT_OF_MEMORY   => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'),
        CURLM_INTERNAL_ERROR  => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!')
    );

    /** @var float */
    protected $selectTimeout;

    public function __construct($selectTimeout = 1.0)
    {
        $this->selectTimeout = $selectTimeout;
        $this->multiHandle = curl_multi_init();
        // @codeCoverageIgnoreStart
        if ($this->multiHandle === false) {
            throw new CurlException('Unable to create multi handle');
        }
        // @codeCoverageIgnoreEnd
        $this->reset();
    }

    public function __destruct()
    {
        if (is_resource($this->multiHandle)) {
            curl_multi_close($this->multiHandle);
        }
    }

    public function add(RequestInterface $request)
    {
        $this->requests[] = $request;
        // If requests are currently transferring and this is async, then the
        // request must be prepared now as the send() method is not called.
        $this->beforeSend($request);
        $this->dispatch(self::ADD_REQUEST, array('request' => $request));

        return $this;
    }

    public function all()
    {
        return $this->requests;
    }

    public function remove(RequestInterface $request)
    {
        $this->removeHandle($request);
        if (($index = array_search($request, $this->requests, true)) !== false) {
            $request = $this->requests[$index];
            unset($this->requests[$index]);
            $this->requests = array_values($this->requests);
            $this->dispatch(self::REMOVE_REQUEST, array('request' => $request));
            return true;
        }

        return false;
    }

    public function reset($hard = false)
    {
        // Remove each request
        if ($this->requests) {
            foreach ($this->requests as $request) {
                $this->remove($request);
            }
        }

        $this->handles = new \SplObjectStorage();
        $this->requests = $this->resourceHash = $this->exceptions = $this->successful = array();
    }

    public function send()
    {
        $this->perform();
        $exceptions = $this->exceptions;
        $successful = $this->successful;
        $this->reset();

        if ($exceptions) {
            $this->throwMultiException($exceptions, $successful);
        }
    }

    public function count()
    {
        return count($this->requests);
    }

    /**
     * Build and throw a MultiTransferException
     *
     * @param array $exceptions Exceptions encountered
     * @param array $successful Successful requests
     * @throws MultiTransferException
     */
    protected function throwMultiException(array $exceptions, array $successful)
    {
        $multiException = new MultiTransferException('Errors during multi transfer');

        while ($e = array_shift($exceptions)) {
            $multiException->addFailedRequestWithException($e['request'], $e['exception']);
        }

        // Add successful requests
        foreach ($successful as $request) {
            if (!$multiException->containsRequest($request)) {
                $multiException->addSuccessfulRequest($request);
            }
        }

        throw $multiException;
    }

    /**
     * Prepare for sending
     *
     * @param RequestInterface $request Request to prepare
     * @throws \Exception on error preparing the request
     */
    protected function beforeSend(RequestInterface $request)
    {
        try {
            $state = $request->setState(RequestInterface::STATE_TRANSFER);
            if ($state == RequestInterface::STATE_TRANSFER) {
                $this->addHandle($request);
            } else {
                // Requests might decide they don't need to be sent just before
                // transfer (e.g. CachePlugin)
                $this->remove($request);
                if ($state == RequestInterface::STATE_COMPLETE) {
                    $this->successful[] = $request;
                }
            }
        } catch (\Exception $e) {
            // Queue the exception to be thrown when sent
            $this->removeErroredRequest($request, $e);
        }
    }

    private function addHandle(RequestInterface $request)
    {
        $handle = $this->createCurlHandle($request)->getHandle();
        $this->checkCurlResult(
            curl_multi_add_handle($this->multiHandle, $handle)
        );
    }

    /**
     * Create a curl handle for a request
     *
     * @param RequestInterface $request Request
     *
     * @return CurlHandle
     */
    protected function createCurlHandle(RequestInterface $request)
    {
        $wrapper = CurlHandle::factory($request);
        $this->handles[$request] = $wrapper;
        $this->resourceHash[(int) $wrapper->getHandle()] = $request;

        return $wrapper;
    }

    /**
     * Get the data from the multi handle
     */
    protected function perform()
    {
        $event = new Event(array('curl_multi' => $this));

        while ($this->requests) {
            // Notify each request as polling
            $blocking = $total = 0;
            foreach ($this->requests as $request) {
                ++$total;
                $event['request'] = $request;
                $request->getEventDispatcher()->dispatch(self::POLLING_REQUEST, $event);
                // The blocking variable just has to be non-falsey to block the loop
                if ($request->getParams()->hasKey(self::BLOCKING)) {
                    ++$blocking;
                }
            }
            if ($blocking == $total) {
                // Sleep to prevent eating CPU because no requests are actually pending a select call
                usleep(500);
            } else {
                $this->executeHandles();
            }
        }
    }

    /**
     * Execute and select curl handles
     */
    private function executeHandles()
    {
        // The first curl_multi_select often times out no matter what, but is usually required for fast transfers
        $selectTimeout = 0.001;
        $active = false;
        do {
            while (($mrc = curl_multi_exec($this->multiHandle, $active)) == CURLM_CALL_MULTI_PERFORM);
            $this->checkCurlResult($mrc);
            $this->processMessages();
            if ($active && curl_multi_select($this->multiHandle, $selectTimeout) === -1) {
                // Perform a usleep if a select returns -1: https://bugs.php.net/bug.php?id=61141
                usleep(150);
            }
            $selectTimeout = $this->selectTimeout;
        } while ($active);
    }

    /**
     * Process any received curl multi messages
     */
    private function processMessages()
    {
        while ($done = curl_multi_info_read($this->multiHandle)) {
            $request = $this->resourceHash[(int) $done['handle']];
            try {
                $this->processResponse($request, $this->handles[$request], $done);
                $this->successful[] = $request;
            } catch (\Exception $e) {
                $this->removeErroredRequest($request, $e);
            }
        }
    }

    /**
     * Remove a request that encountered an exception
     *
     * @param RequestInterface $request Request to remove
     * @param \Exception       $e       Exception encountered
     */
    protected function removeErroredRequest(RequestInterface $request, \Exception $e = null)
    {
        $this->exceptions[] = array('request' => $request, 'exception' => $e);
        $this->remove($request);
        $this->dispatch(self::MULTI_EXCEPTION, array('exception' => $e, 'all_exceptions' => $this->exceptions));
    }

    /**
     * Check for errors and fix headers of a request based on a curl response
     *
     * @param RequestInterface $request Request to process
     * @param CurlHandle       $handle  Curl handle object
     * @param array            $curl    Array returned from curl_multi_info_read
     *
     * @throws CurlException on Curl error
     */
    protected function processResponse(RequestInterface $request, CurlHandle $handle, array $curl)
    {
        // Set the transfer stats on the response
        $handle->updateRequestFromTransfer($request);
        // Check if a cURL exception occurred, and if so, notify things
        $curlException = $this->isCurlException($request, $handle, $curl);

        // Always remove completed curl handles.  They can be added back again
        // via events if needed (e.g. ExponentialBackoffPlugin)
        $this->removeHandle($request);

        if (!$curlException) {
            if ($this->validateResponseWasSet($request)) {
                $state = $request->setState(
                    RequestInterface::STATE_COMPLETE,
                    array('handle' => $handle)
                );
                // Only remove the request if it wasn't resent as a result of
                // the state change
                if ($state != RequestInterface::STATE_TRANSFER) {
                    $this->remove($request);
                }
            }
            return;
        }

        // Set the state of the request to an error
        $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $curlException));
        // Allow things to ignore the error if possible
        if ($state != RequestInterface::STATE_TRANSFER) {
            $this->remove($request);
        }

        // The error was not handled, so fail
        if ($state == RequestInterface::STATE_ERROR) {
            /** @var CurlException $curlException */
            throw $curlException;
        }
    }

    /**
     * Remove a curl handle from the curl multi object
     *
     * @param RequestInterface $request Request that owns the handle
     */
    protected function removeHandle(RequestInterface $request)
    {
        if (isset($this->handles[$request])) {
            $handle = $this->handles[$request];
            curl_multi_remove_handle($this->multiHandle, $handle->getHandle());
            unset($this->handles[$request]);
            unset($this->resourceHash[(int) $handle->getHandle()]);
            $handle->close();
        }
    }

    /**
     * Check if a cURL transfer resulted in what should be an exception
     *
     * @param RequestInterface $request Request to check
     * @param CurlHandle       $handle  Curl handle object
     * @param array            $curl    Array returned from curl_multi_info_read
     *
     * @return CurlException|bool
     */
    private function isCurlException(RequestInterface $request, CurlHandle $handle, array $curl)
    {
        if (CURLM_OK == $curl['result'] || CURLM_CALL_MULTI_PERFORM == $curl['result']) {
            return false;
        }

        $handle->setErrorNo($curl['result']);
        $e = new CurlException(sprintf('[curl] %s: %s [url] %s',
            $handle->getErrorNo(), $handle->getError(), $handle->getUrl()));
        $e->setCurlHandle($handle)
            ->setRequest($request)
            ->setCurlInfo($handle->getInfo())
            ->setError($handle->getError(), $handle->getErrorNo());

        return $e;
    }

    /**
     * Throw an exception for a cURL multi response if needed
     *
     * @param int $code Curl response code
     * @throws CurlException
     */
    private function checkCurlResult($code)
    {
        if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) {
            throw new CurlException(isset($this->multiErrors[$code])
                ? "cURL error: {$code} ({$this->multiErrors[$code][0]}): cURL message: {$this->multiErrors[$code][1]}"
                : 'Unexpected cURL error: ' . $code
            );
        }
    }

    /**
     * @link https://github.com/guzzle/guzzle/issues/710
     */
    private function validateResponseWasSet(RequestInterface $request)
    {
        if ($request->getResponse()) {
            return true;
        }

        $body = $request instanceof EntityEnclosingRequestInterface
            ? $request->getBody()
            : null;

        if (!$body) {
            $rex = new RequestException(
                'No response was received for a request with no body. This'
                . ' could mean that you are saturating your network.'
            );
            $rex->setRequest($request);
            $this->removeErroredRequest($request, $rex);
        } elseif (!$body->isSeekable() || !$body->seek(0)) {
            // Nothing we can do with this. Sorry!
            $rex = new RequestException(
                'The connection was unexpectedly closed. The request would'
                . ' have been retried, but attempting to rewind the'
                . ' request body failed.'
            );
            $rex->setRequest($request);
            $this->removeErroredRequest($request, $rex);
        } else {
            $this->remove($request);
            // Add the request back to the batch to retry automatically.
            $this->requests[] = $request;
            $this->addHandle($request);
        }

        return false;
    }
}
<?php

namespace Guzzle\Http\Curl;

use Guzzle\Common\HasDispatcherInterface;
use Guzzle\Common\Exception\ExceptionCollection;
use Guzzle\Http\Message\RequestInterface;

/**
 * Interface for sending a pool of {@see RequestInterface} objects in parallel
 */
interface CurlMultiInterface extends \Countable, HasDispatcherInterface
{
    const POLLING_REQUEST = 'curl_multi.polling_request';
    const ADD_REQUEST = 'curl_multi.add_request';
    const REMOVE_REQUEST = 'curl_multi.remove_request';
    const MULTI_EXCEPTION = 'curl_multi.exception';
    const BLOCKING = 'curl_multi.blocking';

    /**
     * Add a request to the pool.
     *
     * @param RequestInterface $request Request to add
     *
     * @return CurlMultiInterface
     */
    public function add(RequestInterface $request);

    /**
     * Get an array of attached {@see RequestInterface} objects
     *
     * @return array
     */
    public function all();

    /**
     * Remove a request from the pool.
     *
     * @param RequestInterface $request Request to remove
     *
     * @return bool Returns true on success or false on failure
     */
    public function remove(RequestInterface $request);

    /**
     * Reset the state and remove any attached RequestInterface objects
     *
     * @param bool $hard Set to true to close and reopen any open multi handles
     */
    public function reset($hard = false);

    /**
     * Send a pool of {@see RequestInterface} requests.
     *
     * @throws ExceptionCollection if any requests threw exceptions during the transfer.
     */
    public function send();
}
<?php

namespace Guzzle\Http\Curl;

use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Http\Message\RequestInterface;

/**
 * Proxies requests and connections to a pool of internal curl_multi handles. Each recursive call will add requests
 * to the next available CurlMulti handle.
 */
class CurlMultiProxy extends AbstractHasDispatcher implements CurlMultiInterface
{
    protected $handles = array();
    protected $groups = array();
    protected $queued = array();
    protected $maxHandles;
    protected $selectTimeout;

    /**
     * @param int   $maxHandles The maximum number of idle CurlMulti handles to allow to remain open
     * @param float $selectTimeout timeout for curl_multi_select
     */
    public function __construct($maxHandles = 3, $selectTimeout = 1.0)
    {
        $this->maxHandles = $maxHandles;
        $this->selectTimeout = $selectTimeout;
        // You can get some weird "Too many open files" errors when sending a large amount of requests in parallel.
        // These two statements autoload classes before a system runs out of file descriptors so that you can get back
        // valuable error messages if you run out.
        class_exists('Guzzle\Http\Message\Response');
        class_exists('Guzzle\Http\Exception\CurlException');
    }

    public function add(RequestInterface $request)
    {
        $this->queued[] = $request;

        return $this;
    }

    public function all()
    {
        $requests = $this->queued;
        foreach ($this->handles as $handle) {
            $requests = array_merge($requests, $handle->all());
        }

        return $requests;
    }

    public function remove(RequestInterface $request)
    {
        foreach ($this->queued as $i => $r) {
            if ($request === $r) {
                unset($this->queued[$i]);
                return true;
            }
        }

        foreach ($this->handles as $handle) {
            if ($handle->remove($request)) {
                return true;
            }
        }

        return false;
    }

    public function reset($hard = false)
    {
        $this->queued = array();
        $this->groups = array();
        foreach ($this->handles as $handle) {
            $handle->reset();
        }
        if ($hard) {
            $this->handles = array();
        }

        return $this;
    }

    public function send()
    {
        if ($this->queued) {
            $group = $this->getAvailableHandle();
            // Add this handle to a list of handles than is claimed
            $this->groups[] = $group;
            while ($request = array_shift($this->queued)) {
                $group->add($request);
            }
            try {
                $group->send();
                array_pop($this->groups);
                $this->cleanupHandles();
            } catch (\Exception $e) {
                // Remove the group and cleanup if an exception was encountered and no more requests in group
                if (!$group->count()) {
                    array_pop($this->groups);
                    $this->cleanupHandles();
                }
                throw $e;
            }
        }
    }

    public function count()
    {
        return count($this->all());
    }

    /**
     * Get an existing available CurlMulti handle or create a new one
     *
     * @return CurlMulti
     */
    protected function getAvailableHandle()
    {
        // Grab a handle that is not claimed
        foreach ($this->handles as $h) {
            if (!in_array($h, $this->groups, true)) {
                return $h;
            }
        }

        // All are claimed, so create one
        $handle = new CurlMulti($this->selectTimeout);
        $handle->setEventDispatcher($this->getEventDispatcher());
        $this->handles[] = $handle;

        return $handle;
    }

    /**
     * Trims down unused CurlMulti handles to limit the number of open connections
     */
    protected function cleanupHandles()
    {
        if ($diff = max(0, count($this->handles) - $this->maxHandles)) {
            for ($i = count($this->handles) - 1; $i > 0 && $diff > 0; $i--) {
                if (!count($this->handles[$i])) {
                    unset($this->handles[$i]);
                    $diff--;
                }
            }
            $this->handles = array_values($this->handles);
        }
    }
}
<?php

namespace Guzzle\Http\Curl;

/**
 * Class used for querying curl_version data
 */
class CurlVersion
{
    /** @var array curl_version() information */
    protected $version;

    /** @var CurlVersion */
    protected static $instance;

    /** @var string Default user agent */
    protected $userAgent;

    /**
     * @return CurlVersion
     */
    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * Get all of the curl_version() data
     *
     * @return array
     */
    public function getAll()
    {
        if (!$this->version) {
            $this->version = curl_version();
        }

        return $this->version;
    }

    /**
     * Get a specific type of curl information
     *
     * @param string $type Version information to retrieve. This value is one of:
     *     - version_number:     cURL 24 bit version number
     *     - version:            cURL version number, as a string
     *     - ssl_version_number: OpenSSL 24 bit version number
     *     - ssl_version:        OpenSSL version number, as a string
     *     - libz_version:       zlib version number, as a string
     *     - host:               Information about the host where cURL was built
     *     - features:           A bitmask of the CURL_VERSION_XXX constants
     *     - protocols:          An array of protocols names supported by cURL
     *
     * @return string|float|bool if the $type is found, and false if not found
     */
    public function get($type)
    {
        $version = $this->getAll();

        return isset($version[$type]) ? $version[$type] : false;
    }
}
<?php

namespace Guzzle\Http\Curl;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\EntityBody;
use Guzzle\Http\Message\Response;

/**
 * Mediator between curl handles and request objects
 */
class RequestMediator
{
    /** @var RequestInterface */
    protected $request;

    /** @var bool Whether or not to emit read/write events */
    protected $emitIo;

    /**
     * @param RequestInterface $request Request to mediate
     * @param bool             $emitIo  Set to true to dispatch events on input and output
     */
    public function __construct(RequestInterface $request, $emitIo = false)
    {
        $this->request = $request;
        $this->emitIo = $emitIo;
    }

    /**
     * Receive a response header from curl
     *
     * @param resource $curl   Curl handle
     * @param string   $header Received header
     *
     * @return int
     */
    public function receiveResponseHeader($curl, $header)
    {
        static $normalize = array("\r", "\n");
        $length = strlen($header);
        $header = str_replace($normalize, '', $header);

        if (strpos($header, 'HTTP/') === 0) {

            $startLine = explode(' ', $header, 3);
            $code = $startLine[1];
            $status = isset($startLine[2]) ? $startLine[2] : '';

            // Only download the body of the response to the specified response
            // body when a successful response is received.
            if ($code >= 200 && $code < 300) {
                $body = $this->request->getResponseBody();
            } else {
                $body = EntityBody::factory();
            }

            $response = new Response($code, null, $body);
            $response->setStatus($code, $status);
            $this->request->startResponse($response);

            $this->request->dispatch('request.receive.status_line', array(
                'request'       => $this,
                'line'          => $header,
                'status_code'   => $code,
                'reason_phrase' => $status
            ));

        } elseif ($pos = strpos($header, ':')) {
            $this->request->getResponse()->addHeader(
                trim(substr($header, 0, $pos)),
                trim(substr($header, $pos + 1))
            );
        }

        return $length;
    }

    /**
     * Received a progress notification
     *
     * @param int        $downloadSize Total download size
     * @param int        $downloaded   Amount of bytes downloaded
     * @param int        $uploadSize   Total upload size
     * @param int        $uploaded     Amount of bytes uploaded
     * @param resource   $handle       CurlHandle object
     */
    public function progress($downloadSize, $downloaded, $uploadSize, $uploaded, $handle = null)
    {
        $this->request->dispatch('curl.callback.progress', array(
            'request'       => $this->request,
            'handle'        => $handle,
            'download_size' => $downloadSize,
            'downloaded'    => $downloaded,
            'upload_size'   => $uploadSize,
            'uploaded'      => $uploaded
        ));
    }

    /**
     * Write data to the response body of a request
     *
     * @param resource $curl  Curl handle
     * @param string   $write Data that was received
     *
     * @return int
     */
    public function writeResponseBody($curl, $write)
    {
        if ($this->emitIo) {
            $this->request->dispatch('curl.callback.write', array(
                'request' => $this->request,
                'write'   => $write
            ));
        }

        if ($response = $this->request->getResponse()) {
            return $response->getBody()->write($write);
        } else {
            // Unexpected data received before response headers - abort transfer
            return 0;
        }
    }

    /**
     * Read data from the request body and send it to curl
     *
     * @param resource $ch     Curl handle
     * @param resource $fd     File descriptor
     * @param int      $length Amount of data to read
     *
     * @return string
     */
    public function readRequestBody($ch, $fd, $length)
    {
        if (!($body = $this->request->getBody())) {
            return '';
        }

        $read = (string) $body->read($length);
        if ($this->emitIo) {
            $this->request->dispatch('curl.callback.read', array('request' => $this->request, 'read' => $read));
        }

        return $read;
    }
}
<?php

namespace Guzzle\Http;

use Guzzle\Common\Version;
use Guzzle\Stream\Stream;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Mimetypes;

/**
 * Entity body used with an HTTP request or response
 */
class EntityBody extends Stream implements EntityBodyInterface
{
    /** @var bool Content-Encoding of the entity body if known */
    protected $contentEncoding = false;

    /** @var callable Method to invoke for rewinding a stream */
    protected $rewindFunction;

    /**
     * Create a new EntityBody based on the input type
     *
     * @param resource|string|EntityBody $resource Entity body data
     * @param int                        $size     Size of the data contained in the resource
     *
     * @return EntityBody
     * @throws InvalidArgumentException if the $resource arg is not a resource or string
     */
    public static function factory($resource = '', $size = null)
    {
        if ($resource instanceof EntityBodyInterface) {
            return $resource;
        }

        switch (gettype($resource)) {
            case 'string':
                return self::fromString($resource);
            case 'resource':
                return new static($resource, $size);
            case 'object':
                if (method_exists($resource, '__toString')) {
                    return self::fromString((string) $resource);
                }
                break;
            case 'array':
                return self::fromString(http_build_query($resource));
        }

        throw new InvalidArgumentException('Invalid resource type');
    }

    public function setRewindFunction($callable)
    {
        if (!is_callable($callable)) {
            throw new InvalidArgumentException('Must specify a callable');
        }

        $this->rewindFunction = $callable;

        return $this;
    }

    public function rewind()
    {
        return $this->rewindFunction ? call_user_func($this->rewindFunction, $this) : parent::rewind();
    }

    /**
     * Create a new EntityBody from a string
     *
     * @param string $string String of data
     *
     * @return EntityBody
     */
    public static function fromString($string)
    {
        $stream = fopen('php://temp', 'r+');
        if ($string !== '') {
            fwrite($stream, $string);
            rewind($stream);
        }

        return new static($stream);
    }

    public function compress($filter = 'zlib.deflate')
    {
        $result = $this->handleCompression($filter);
        $this->contentEncoding = $result ? $filter : false;

        return $result;
    }

    public function uncompress($filter = 'zlib.inflate')
    {
        $offsetStart = 0;

        // When inflating gzipped data, the first 10 bytes must be stripped
        // if a gzip header is present
        if ($filter == 'zlib.inflate') {
            // @codeCoverageIgnoreStart
            if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) {
                return false;
            }
            // @codeCoverageIgnoreEnd
            if (stream_get_contents($this->stream, 3, 0) === "\x1f\x8b\x08") {
                $offsetStart = 10;
            }
        }

        $this->contentEncoding = false;

        return $this->handleCompression($filter, $offsetStart);
    }

    public function getContentLength()
    {
        return $this->getSize();
    }

    public function getContentType()
    {
        return $this->getUri() ? Mimetypes::getInstance()->fromFilename($this->getUri()) : null;
    }

    public function getContentMd5($rawOutput = false, $base64Encode = false)
    {
        if ($hash = self::getHash($this, 'md5', $rawOutput)) {
            return $hash && $base64Encode ? base64_encode($hash) : $hash;
        } else {
            return false;
        }
    }

    /**
     * Calculate the MD5 hash of an entity body
     *
     * @param EntityBodyInterface $body         Entity body to calculate the hash for
     * @param bool                $rawOutput    Whether or not to use raw output
     * @param bool                $base64Encode Whether or not to base64 encode raw output (only if raw output is true)
     *
     * @return bool|string Returns an MD5 string on success or FALSE on failure
     * @deprecated This will be deprecated soon
     * @codeCoverageIgnore
     */
    public static function calculateMd5(EntityBodyInterface $body, $rawOutput = false, $base64Encode = false)
    {
        Version::warn(__CLASS__ . ' is deprecated. Use getContentMd5()');
        return $body->getContentMd5($rawOutput, $base64Encode);
    }

    public function setStreamFilterContentEncoding($streamFilterContentEncoding)
    {
        $this->contentEncoding = $streamFilterContentEncoding;

        return $this;
    }

    public function getContentEncoding()
    {
        return strtr($this->contentEncoding, array(
            'zlib.deflate' => 'gzip',
            'bzip2.compress' => 'compress'
        )) ?: false;
    }

    protected function handleCompression($filter, $offsetStart = 0)
    {
        // @codeCoverageIgnoreStart
        if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) {
            return false;
        }
        // @codeCoverageIgnoreEnd

        $handle = fopen('php://temp', 'r+');
        $filter = @stream_filter_append($handle, $filter, STREAM_FILTER_WRITE);
        if (!$filter) {
            return false;
        }

        // Seek to the offset start if possible
        $this->seek($offsetStart);
        while ($data = fread($this->stream, 8096)) {
            fwrite($handle, $data);
        }

        fclose($this->stream);
        $this->stream = $handle;
        stream_filter_remove($filter);
        $stat = fstat($this->stream);
        $this->size = $stat['size'];
        $this->rebuildCache();
        $this->seek(0);

        // Remove any existing rewind function as the underlying stream has been replaced
        $this->rewindFunction = null;

        return true;
    }
}
<?php

namespace Guzzle\Http;

use Guzzle\Stream\StreamInterface;

/**
 * Entity body used with an HTTP request or response
 */
interface EntityBodyInterface extends StreamInterface
{
    /**
     * Specify a custom callback used to rewind a non-seekable stream. This can be useful entity enclosing requests
     * that are redirected.
     *
     * @param mixed $callable Callable to invoke to rewind a non-seekable stream. The callback must accept an
     *                        EntityBodyInterface object, perform the rewind if possible, and return a boolean
     *                        representing whether or not the rewind was successful.
     * @return self
     */
    public function setRewindFunction($callable);

    /**
     * If the stream is readable, compress the data in the stream using deflate compression. The uncompressed stream is
     * then closed, and the compressed stream then becomes the wrapped stream.
     *
     * @param string $filter Compression filter
     *
     * @return bool Returns TRUE on success or FALSE on failure
     */
    public function compress($filter = 'zlib.deflate');

    /**
     * Decompress a deflated string. Once uncompressed, the uncompressed string is then used as the wrapped stream.
     *
     * @param string $filter De-compression filter
     *
     * @return bool Returns TRUE on success or FALSE on failure
     */
    public function uncompress($filter = 'zlib.inflate');

    /**
     * Get the Content-Length of the entity body if possible (alias of getSize)
     *
     * @return int|bool Returns the Content-Length or false on failure
     */
    public function getContentLength();

    /**
     * Guess the Content-Type of a local stream
     *
     * @return string|null
     * @see http://www.php.net/manual/en/function.finfo-open.php
     */
    public function getContentType();

    /**
     * Get an MD5 checksum of the stream's contents
     *
     * @param bool $rawOutput    Whether or not to use raw output
     * @param bool $base64Encode Whether or not to base64 encode raw output (only if raw output is true)
     *
     * @return bool|string Returns an MD5 string on success or FALSE on failure
     */
    public function getContentMd5($rawOutput = false, $base64Encode = false);

    /**
     * Get the Content-Encoding of the EntityBody
     *
     * @return bool|string
     */
    public function getContentEncoding();
}
<?php

namespace Guzzle\Http\Exception;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Http request exception thrown when a bad response is received
 */
class BadResponseException extends RequestException
{
    /** @var Response */
    private $response;

    /**
     * Factory method to create a new response exception based on the response code.
     *
     * @param RequestInterface $request  Request
     * @param Response         $response Response received
     *
     * @return BadResponseException
     */
    public static function factory(RequestInterface $request, Response $response)
    {
        if ($response->isClientError()) {
            $label = 'Client error response';
            $class = __NAMESPACE__ . '\\ClientErrorResponseException';
        } elseif ($response->isServerError()) {
            $label = 'Server error response';
            $class = __NAMESPACE__ . '\\ServerErrorResponseException';
        } else {
            $label = 'Unsuccessful response';
            $class = __CLASS__;
        }

        $message = $label . PHP_EOL . implode(PHP_EOL, array(
            '[status code] ' . $response->getStatusCode(),
            '[reason phrase] ' . $response->getReasonPhrase(),
            '[url] ' . $request->getUrl(),
        ));

        $e = new $class($message);
        $e->setResponse($response);
        $e->setRequest($request);

        return $e;
    }

    /**
     * Set the response that caused the exception
     *
     * @param Response $response Response to set
     */
    public function setResponse(Response $response)
    {
        $this->response = $response;
    }

    /**
     * Get the response that caused the exception
     *
     * @return Response
     */
    public function getResponse()
    {
        return $this->response;
    }
}
<?php

namespace Guzzle\Http\Exception;

/**
 * Exception when a client error is encountered (4xx codes)
 */
class ClientErrorResponseException extends BadResponseException {}
<?php

namespace Guzzle\Http\Exception;

use Guzzle\Common\Exception\RuntimeException;

class CouldNotRewindStreamException extends RuntimeException implements HttpException {}
<?php

namespace Guzzle\Http\Exception;

use Guzzle\Http\Curl\CurlHandle;

/**
 * cURL request exception
 */
class CurlException extends RequestException
{
    private $curlError;
    private $curlErrorNo;
    private $handle;
    private $curlInfo = array();

    /**
     * Set the cURL error message
     *
     * @param string $error  Curl error
     * @param int    $number Curl error number
     *
     * @return self
     */
    public function setError($error, $number)
    {
        $this->curlError = $error;
        $this->curlErrorNo = $number;

        return $this;
    }

    /**
     * Set the associated curl handle
     *
     * @param CurlHandle $handle Curl handle
     *
     * @return self
     */
    public function setCurlHandle(CurlHandle $handle)
    {
        $this->handle = $handle;

        return $this;
    }

    /**
     * Get the associated cURL handle
     *
     * @return CurlHandle|null
     */
    public function getCurlHandle()
    {
        return $this->handle;
    }

    /**
     * Get the associated cURL error message
     *
     * @return string|null
     */
    public function getError()
    {
        return $this->curlError;
    }

    /**
     * Get the associated cURL error number
     *
     * @return int|null
     */
    public function getErrorNo()
    {
        return $this->curlErrorNo;
    }

    /**
     * Returns curl information about the transfer
     *
     * @return array
     */
    public function getCurlInfo()
    {
        return $this->curlInfo;
    }

    /**
     * Set curl transfer information
     *
     * @param array $info Array of curl transfer information
     *
     * @return self
     * @link http://php.net/manual/en/function.curl-getinfo.php
     */
    public function setCurlInfo(array $info)
    {
        $this->curlInfo = $info;

        return $this;
    }
}
<?php

namespace Guzzle\Http\Exception;

use Guzzle\Common\Exception\GuzzleException;

/**
 * Http exception interface
 */
interface HttpException extends GuzzleException {}
<?php

namespace Guzzle\Http\Exception;

use Guzzle\Common\Exception\ExceptionCollection;
use Guzzle\Http\Message\RequestInterface;

/**
 * Exception encountered during a multi transfer
 */
class MultiTransferException extends ExceptionCollection
{
    protected $successfulRequests = array();
    protected $failedRequests = array();
    protected $exceptionForRequest = array();

    /**
     * Get all of the requests in the transfer
     *
     * @return array
     */
    public function getAllRequests()
    {
        return array_merge($this->successfulRequests, $this->failedRequests);
    }

    /**
     * Add to the array of successful requests
     *
     * @param RequestInterface $request Successful request
     *
     * @return self
     */
    public function addSuccessfulRequest(RequestInterface $request)
    {
        $this->successfulRequests[] = $request;

        return $this;
    }

    /**
     * Add to the array of failed requests
     *
     * @param RequestInterface $request Failed request
     *
     * @return self
     */
    public function addFailedRequest(RequestInterface $request)
    {
        $this->failedRequests[] = $request;

        return $this;
    }

    /**
     * Add to the array of failed requests and associate with exceptions
     *
     * @param RequestInterface $request   Failed request
     * @param \Exception       $exception Exception to add and associate with
     *
     * @return self
     */
    public function addFailedRequestWithException(RequestInterface $request, \Exception $exception)
    {
        $this->add($exception)
             ->addFailedRequest($request)
             ->exceptionForRequest[spl_object_hash($request)] = $exception;

        return $this;
    }

    /**
     * Get the Exception that caused the given $request to fail
     *
     * @param RequestInterface $request Failed command
     *
     * @return \Exception|null
     */
    public function getExceptionForFailedRequest(RequestInterface $request)
    {
        $oid = spl_object_hash($request);

        return isset($this->exceptionForRequest[$oid]) ? $this->exceptionForRequest[$oid] : null;
    }

    /**
     * Set all of the successful requests
     *
     * @param array Array of requests
     *
     * @return self
     */
    public function setSuccessfulRequests(array $requests)
    {
        $this->successfulRequests = $requests;

        return $this;
    }

    /**
     * Set all of the failed requests
     *
     * @param array Array of requests
     *
     * @return self
     */
    public function setFailedRequests(array $requests)
    {
        $this->failedRequests = $requests;

        return $this;
    }

    /**
     * Get an array of successful requests sent in the multi transfer
     *
     * @return array
     */
    public function getSuccessfulRequests()
    {
        return $this->successfulRequests;
    }

    /**
     * Get an array of failed requests sent in the multi transfer
     *
     * @return array
     */
    public function getFailedRequests()
    {
        return $this->failedRequests;
    }

    /**
     * Check if the exception object contains a request
     *
     * @param RequestInterface $request Request to check
     *
     * @return bool
     */
    public function containsRequest(RequestInterface $request)
    {
        return in_array($request, $this->failedRequests, true) || in_array($request, $this->successfulRequests, true);
    }
}
<?php

namespace Guzzle\Http\Exception;

use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Http\Message\RequestInterface;

/**
 * Http request exception
 */
class RequestException extends RuntimeException implements HttpException
{
    /** @var RequestInterface */
    protected $request;

    /**
     * Set the request that caused the exception
     *
     * @param RequestInterface $request Request to set
     *
     * @return RequestException
     */
    public function setRequest(RequestInterface $request)
    {
        $this->request = $request;

        return $this;
    }

    /**
     * Get the request that caused the exception
     *
     * @return RequestInterface
     */
    public function getRequest()
    {
        return $this->request;
    }
}
<?php

namespace Guzzle\Http\Exception;

/**
 * Exception when a server error is encountered (5xx codes)
 */
class ServerErrorResponseException extends BadResponseException {}
<?php

namespace Guzzle\Http\Exception;

class TooManyRedirectsException extends BadResponseException {}
<?php

namespace Guzzle\Http;

use Guzzle\Common\Event;
use Guzzle\Common\HasDispatcherInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * EntityBody decorator that emits events for read and write methods
 */
class IoEmittingEntityBody extends AbstractEntityBodyDecorator implements HasDispatcherInterface
{
    /** @var EventDispatcherInterface */
    protected $eventDispatcher;

    public static function getAllEvents()
    {
        return array('body.read', 'body.write');
    }

    /**
     * {@inheritdoc}
     * @codeCoverageIgnore
     */
    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
    {
        $this->eventDispatcher = $eventDispatcher;

        return $this;
    }

    public function getEventDispatcher()
    {
        if (!$this->eventDispatcher) {
            $this->eventDispatcher = new EventDispatcher();
        }

        return $this->eventDispatcher;
    }

    public function dispatch($eventName, array $context = array())
    {
        return $this->getEventDispatcher()->dispatch($eventName, new Event($context));
    }

    /**
     * {@inheritdoc}
     * @codeCoverageIgnore
     */
    public function addSubscriber(EventSubscriberInterface $subscriber)
    {
        $this->getEventDispatcher()->addSubscriber($subscriber);

        return $this;
    }

    public function read($length)
    {
        $event = array(
            'body'   => $this,
            'length' => $length,
            'read'   => $this->body->read($length)
        );
        $this->dispatch('body.read', $event);

        return $event['read'];
    }

    public function write($string)
    {
        $event = array(
            'body'   => $this,
            'write'  => $string,
            'result' => $this->body->write($string)
        );
        $this->dispatch('body.write', $event);

        return $event['result'];
    }
}
<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Version;
use Guzzle\Common\Collection;
use Guzzle\Http\Message\Header\HeaderCollection;
use Guzzle\Http\Message\Header\HeaderFactory;
use Guzzle\Http\Message\Header\HeaderFactoryInterface;
use Guzzle\Http\Message\Header\HeaderInterface;

/**
 * Abstract HTTP request/response message
 */
abstract class AbstractMessage implements MessageInterface
{
    /** @var array HTTP header collection */
    protected $headers;

    /** @var HeaderFactoryInterface $headerFactory */
    protected $headerFactory;

    /** @var Collection Custom message parameters that are extendable by plugins */
    protected $params;

    /** @var string Message protocol */
    protected $protocol = 'HTTP';

    /** @var string HTTP protocol version of the message */
    protected $protocolVersion = '1.1';

    public function __construct()
    {
        $this->params = new Collection();
        $this->headerFactory = new HeaderFactory();
        $this->headers = new HeaderCollection();
    }

    /**
     * Set the header factory to use to create headers
     *
     * @param HeaderFactoryInterface $factory
     *
     * @return self
     */
    public function setHeaderFactory(HeaderFactoryInterface $factory)
    {
        $this->headerFactory = $factory;

        return $this;
    }

    public function getParams()
    {
        return $this->params;
    }

    public function addHeader($header, $value)
    {
        if (isset($this->headers[$header])) {
            $this->headers[$header]->add($value);
        } elseif ($value instanceof HeaderInterface) {
            $this->headers[$header] = $value;
        } else {
            $this->headers[$header] = $this->headerFactory->createHeader($header, $value);
        }

        return $this;
    }

    public function addHeaders(array $headers)
    {
        foreach ($headers as $key => $value) {
            $this->addHeader($key, $value);
        }

        return $this;
    }

    public function getHeader($header)
    {
        return $this->headers[$header];
    }

    public function getHeaders()
    {
        return $this->headers;
    }

    public function getHeaderLines()
    {
        $headers = array();
        foreach ($this->headers as $value) {
            $headers[] = $value->getName() . ': ' . $value;
        }

        return $headers;
    }

    public function setHeader($header, $value)
    {
        unset($this->headers[$header]);
        $this->addHeader($header, $value);

        return $this;
    }

    public function setHeaders(array $headers)
    {
        $this->headers->clear();
        foreach ($headers as $key => $value) {
            $this->addHeader($key, $value);
        }

        return $this;
    }

    public function hasHeader($header)
    {
        return isset($this->headers[$header]);
    }

    public function removeHeader($header)
    {
        unset($this->headers[$header]);

        return $this;
    }

    /**
     * @deprecated Use $message->getHeader()->parseParams()
     * @codeCoverageIgnore
     */
    public function getTokenizedHeader($header, $token = ';')
    {
        Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader()->parseParams()');
        if ($this->hasHeader($header)) {
            $data = new Collection();
            foreach ($this->getHeader($header)->parseParams() as $values) {
                foreach ($values as $key => $value) {
                    if ($value === '') {
                        $data->set($data->count(), $key);
                    } else {
                        $data->add($key, $value);
                    }
                }
            }
            return $data;
        }
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function setTokenizedHeader($header, $data, $token = ';')
    {
        Version::warn(__METHOD__ . ' is deprecated.');
        return $this;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function getCacheControlDirective($directive)
    {
        Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->getDirective()');
        if (!($header = $this->getHeader('Cache-Control'))) {
            return null;
        }

        return $header->getDirective($directive);
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function hasCacheControlDirective($directive)
    {
        Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->hasDirective()');
        if ($header = $this->getHeader('Cache-Control')) {
            return $header->hasDirective($directive);
        } else {
            return false;
        }
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function addCacheControlDirective($directive, $value = true)
    {
        Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->addDirective()');
        if (!($header = $this->getHeader('Cache-Control'))) {
            $this->addHeader('Cache-Control', '');
            $header = $this->getHeader('Cache-Control');
        }

        $header->addDirective($directive, $value);

        return $this;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function removeCacheControlDirective($directive)
    {
        Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->removeDirective()');
        if ($header = $this->getHeader('Cache-Control')) {
            $header->removeDirective($directive);
        }

        return $this;
    }
}
<?php

namespace Guzzle\Http\Message;

use Guzzle\Http\EntityBody;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\QueryString;
use Guzzle\Http\RedirectPlugin;
use Guzzle\Http\Exception\RequestException;

/**
 * HTTP request that sends an entity-body in the request message (POST, PUT, PATCH, DELETE)
 */
class EntityEnclosingRequest extends Request implements EntityEnclosingRequestInterface
{
    /** @var int When the size of the body is greater than 1MB, then send Expect: 100-Continue */
    protected $expectCutoff = 1048576;

    /** @var EntityBodyInterface $body Body of the request */
    protected $body;

    /** @var QueryString POST fields to use in the EntityBody */
    protected $postFields;

    /** @var array POST files to send with the request */
    protected $postFiles = array();

    public function __construct($method, $url, $headers = array())
    {
        $this->postFields = new QueryString();
        parent::__construct($method, $url, $headers);
    }

    /**
     * @return string
     */
    public function __toString()
    {
        // Only attempt to include the POST data if it's only fields
        if (count($this->postFields) && empty($this->postFiles)) {
            return parent::__toString() . (string) $this->postFields;
        }

        return parent::__toString() . $this->body;
    }

    public function setState($state, array $context = array())
    {
        parent::setState($state, $context);
        if ($state == self::STATE_TRANSFER && !$this->body && !count($this->postFields) && !count($this->postFiles)) {
            $this->setHeader('Content-Length', 0)->removeHeader('Transfer-Encoding');
        }

        return $this->state;
    }

    public function setBody($body, $contentType = null)
    {
        $this->body = EntityBody::factory($body);

        // Auto detect the Content-Type from the path of the request if possible
        if ($contentType === null && !$this->hasHeader('Content-Type')) {
            $contentType = $this->body->getContentType();
        }

        if ($contentType) {
            $this->setHeader('Content-Type', $contentType);
        }

        // Always add the Expect 100-Continue header if the body cannot be rewound. This helps with redirects.
        if (!$this->body->isSeekable() && $this->expectCutoff !== false) {
            $this->setHeader('Expect', '100-Continue');
        }

        // Set the Content-Length header if it can be determined
        $size = $this->body->getContentLength();
        if ($size !== null && $size !== false) {
            $this->setHeader('Content-Length', $size);
            if ($size > $this->expectCutoff) {
                $this->setHeader('Expect', '100-Continue');
            }
        } elseif (!$this->hasHeader('Content-Length')) {
            if ('1.1' == $this->protocolVersion) {
                $this->setHeader('Transfer-Encoding', 'chunked');
            } else {
                throw new RequestException(
                    'Cannot determine Content-Length and cannot use chunked Transfer-Encoding when using HTTP/1.0'
                );
            }
        }

        return $this;
    }

    public function getBody()
    {
        return $this->body;
    }

    /**
     * Set the size that the entity body of the request must exceed before adding the Expect: 100-Continue header.
     *
     * @param int|bool $size Cutoff in bytes. Set to false to never send the expect header (even with non-seekable data)
     *
     * @return self
     */
    public function setExpectHeaderCutoff($size)
    {
        $this->expectCutoff = $size;
        if ($size === false || !$this->body) {
            $this->removeHeader('Expect');
        } elseif ($this->body && $this->body->getSize() && $this->body->getSize() > $size) {
            $this->setHeader('Expect', '100-Continue');
        }

        return $this;
    }

    public function configureRedirects($strict = false, $maxRedirects = 5)
    {
        $this->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, $strict);
        if ($maxRedirects == 0) {
            $this->getParams()->set(RedirectPlugin::DISABLE, true);
        } else {
            $this->getParams()->set(RedirectPlugin::MAX_REDIRECTS, $maxRedirects);
        }

        return $this;
    }

    public function getPostField($field)
    {
        return $this->postFields->get($field);
    }

    public function getPostFields()
    {
        return $this->postFields;
    }

    public function setPostField($key, $value)
    {
        $this->postFields->set($key, $value);
        $this->processPostFields();

        return $this;
    }

    public function addPostFields($fields)
    {
        $this->postFields->merge($fields);
        $this->processPostFields();

        return $this;
    }

    public function removePostField($field)
    {
        $this->postFields->remove($field);
        $this->processPostFields();

        return $this;
    }

    public function getPostFiles()
    {
        return $this->postFiles;
    }

    public function getPostFile($fieldName)
    {
        return isset($this->postFiles[$fieldName]) ? $this->postFiles[$fieldName] : null;
    }

    public function removePostFile($fieldName)
    {
        unset($this->postFiles[$fieldName]);
        $this->processPostFields();

        return $this;
    }

    public function addPostFile($field, $filename = null, $contentType = null, $postname = null)
    {
        $data = null;

        if ($field instanceof PostFileInterface) {
            $data = $field;
        } elseif (is_array($filename)) {
            // Allow multiple values to be set in a single key
            foreach ($filename as $file) {
                $this->addPostFile($field, $file, $contentType);
            }
            return $this;
        } elseif (!is_string($filename)) {
            throw new RequestException('The path to a file must be a string');
        } elseif (!empty($filename)) {
            // Adding an empty file will cause cURL to error out
            $data = new PostFile($field, $filename, $contentType, $postname);
        }

        if ($data) {
            if (!isset($this->postFiles[$data->getFieldName()])) {
                $this->postFiles[$data->getFieldName()] = array($data);
            } else {
                $this->postFiles[$data->getFieldName()][] = $data;
            }
            $this->processPostFields();
        }

        return $this;
    }

    public function addPostFiles(array $files)
    {
        foreach ($files as $key => $file) {
            if ($file instanceof PostFileInterface) {
                $this->addPostFile($file, null, null, false);
            } elseif (is_string($file)) {
                // Convert non-associative array keys into 'file'
                if (is_numeric($key)) {
                    $key = 'file';
                }
                $this->addPostFile($key, $file, null, false);
            } else {
                throw new RequestException('File must be a string or instance of PostFileInterface');
            }
        }

        return $this;
    }

    /**
     * Determine what type of request should be sent based on post fields
     */
    protected function processPostFields()
    {
        if (!$this->postFiles) {
            $this->removeHeader('Expect')->setHeader('Content-Type', self::URL_ENCODED);
        } else {
            $this->setHeader('Content-Type', self::MULTIPART);
            if ($this->expectCutoff !== false) {
                $this->setHeader('Expect', '100-Continue');
            }
        }
    }
}
<?php

namespace Guzzle\Http\Message;

use Guzzle\Http\Exception\RequestException;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\QueryString;

/**
 * HTTP request that sends an entity-body in the request message (POST, PUT)
 */
interface EntityEnclosingRequestInterface extends RequestInterface
{
    const URL_ENCODED = 'application/x-www-form-urlencoded; charset=utf-8';
    const MULTIPART = 'multipart/form-data';

    /**
     * Set the body of the request
     *
     * @param string|resource|EntityBodyInterface $body        Body to use in the entity body of the request
     * @param string                              $contentType Content-Type to set. Leave null to use an existing
     *                                                         Content-Type or to guess the Content-Type
     * @return self
     * @throws RequestException if the protocol is < 1.1 and Content-Length can not be determined
     */
    public function setBody($body, $contentType = null);

    /**
     * Get the body of the request if set
     *
     * @return EntityBodyInterface|null
     */
    public function getBody();

    /**
     * Get a POST field from the request
     *
     * @param string $field Field to retrieve
     *
     * @return mixed|null
     */
    public function getPostField($field);

    /**
     * Get the post fields that will be used in the request
     *
     * @return QueryString
     */
    public function getPostFields();

    /**
     * Set a POST field value
     *
     * @param string $key   Key to set
     * @param string $value Value to set
     *
     * @return self
     */
    public function setPostField($key, $value);

    /**
     * Add POST fields to use in the request
     *
     * @param QueryString|array $fields POST fields
     *
     * @return self
     */
    public function addPostFields($fields);

    /**
     * Remove a POST field or file by name
     *
     * @param string $field Name of the POST field or file to remove
     *
     * @return self
     */
    public function removePostField($field);

    /**
     * Returns an associative array of POST field names to PostFileInterface objects
     *
     * @return array
     */
    public function getPostFiles();

    /**
     * Get a POST file from the request
     *
     * @param string $fieldName POST fields to retrieve
     *
     * @return array|null Returns an array wrapping an array of PostFileInterface objects
     */
    public function getPostFile($fieldName);

    /**
     * Remove a POST file from the request
     *
     * @param string $fieldName POST file field name to remove
     *
     * @return self
     */
    public function removePostFile($fieldName);

    /**
     * Add a POST file to the upload
     *
     * @param string $field       POST field to use (e.g. file). Used to reference content from the server.
     * @param string $filename    Full path to the file. Do not include the @ symbol.
     * @param string $contentType Optional Content-Type to add to the Content-Disposition.
     *                            Default behavior is to guess. Set to false to not specify.
     * @param string $postname    The name of the file, when posted. (e.g. rename the file)
     * @return self
     */
    public function addPostFile($field, $filename = null, $contentType = null, $postname = null);

    /**
     * Add POST files to use in the upload
     *
     * @param array $files An array of POST fields => filenames where filename can be a string or PostFileInterface
     *
     * @return self
     */
    public function addPostFiles(array $files);

    /**
     * Configure how redirects are handled for the request
     *
     * @param bool $strict       Set to true to follow strict RFC compliance when redirecting POST requests. Most
     *                           browsers with follow a 301-302 redirect for a POST request with a GET request. This is
     *                           the default behavior of Guzzle. Enable strict redirects to redirect these responses
     *                           with a POST rather than a GET request.
     * @param int  $maxRedirects Specify the maximum number of allowed redirects. Set to 0 to disable redirects.
     *
     * @return self
     */
    public function configureRedirects($strict = false, $maxRedirects = 5);
}
<?php

namespace Guzzle\Http\Message\Header;

use Guzzle\Http\Message\Header;

/**
 * Provides helpful functionality for Cache-Control headers
 */
class CacheControl extends Header
{
    /** @var array */
    protected $directives;

    public function add($value)
    {
        parent::add($value);
        $this->directives = null;
    }

    public function removeValue($searchValue)
    {
        parent::removeValue($searchValue);
        $this->directives = null;
    }

    /**
     * Check if a specific cache control directive exists
     *
     * @param string $param Directive to retrieve
     *
     * @return bool
     */
    public function hasDirective($param)
    {
        $directives = $this->getDirectives();

        return isset($directives[$param]);
    }

    /**
     * Get a specific cache control directive
     *
     * @param string $param Directive to retrieve
     *
     * @return string|bool|null
     */
    public function getDirective($param)
    {
        $directives = $this->getDirectives();

        return isset($directives[$param]) ? $directives[$param] : null;
    }

    /**
     * Add a cache control directive
     *
     * @param string $param Directive to add
     * @param string $value Value to set
     *
     * @return self
     */
    public function addDirective($param, $value)
    {
        $directives = $this->getDirectives();
        $directives[$param] = $value;
        $this->updateFromDirectives($directives);

        return $this;
    }

    /**
     * Remove a cache control directive by name
     *
     * @param string $param Directive to remove
     *
     * @return self
     */
    public function removeDirective($param)
    {
        $directives = $this->getDirectives();
        unset($directives[$param]);
        $this->updateFromDirectives($directives);

        return $this;
    }

    /**
     * Get an associative array of cache control directives
     *
     * @return array
     */
    public function getDirectives()
    {
        if ($this->directives === null) {
            $this->directives = array();
            foreach ($this->parseParams() as $collection) {
                foreach ($collection as $key => $value) {
                    $this->directives[$key] = $value === '' ? true : $value;
                }
            }
        }

        return $this->directives;
    }

    /**
     * Updates the header value based on the parsed directives
     *
     * @param array $directives Array of cache control directives
     */
    protected function updateFromDirectives(array $directives)
    {
        $this->directives = $directives;
        $this->values = array();

        foreach ($directives as $key => $value) {
            $this->values[] = $value === true ? $key : "{$key}={$value}";
        }
    }
}
<?php

namespace Guzzle\Http\Message\Header;

use Guzzle\Common\ToArrayInterface;

/**
 * Provides a case-insensitive collection of headers
 */
class HeaderCollection implements \IteratorAggregate, \Countable, \ArrayAccess, ToArrayInterface
{
    /** @var array */
    protected $headers;

    public function __construct($headers = array())
    {
        $this->headers = $headers;
    }

    public function __clone()
    {
        foreach ($this->headers as &$header) {
            $header = clone $header;
        }
    }

    /**
     * Clears the header collection
     */
    public function clear()
    {
        $this->headers = array();
    }

    /**
     * Set a header on the collection
     *
     * @param HeaderInterface $header Header to add
     *
     * @return self
     */
    public function add(HeaderInterface $header)
    {
        $this->headers[strtolower($header->getName())] = $header;

        return $this;
    }

    /**
     * Get an array of header objects
     *
     * @return array
     */
    public function getAll()
    {
        return $this->headers;
    }

    /**
     * Alias of offsetGet
     */
    public function get($key)
    {
        return $this->offsetGet($key);
    }

    public function count()
    {
        return count($this->headers);
    }

    public function offsetExists($offset)
    {
        return isset($this->headers[strtolower($offset)]);
    }

    public function offsetGet($offset)
    {
        $l = strtolower($offset);

        return isset($this->headers[$l]) ? $this->headers[$l] : null;
    }

    public function offsetSet($offset, $value)
    {
        $this->add($value);
    }

    public function offsetUnset($offset)
    {
        unset($this->headers[strtolower($offset)]);
    }

    public function getIterator()
    {
        return new \ArrayIterator($this->headers);
    }

    public function toArray()
    {
        $result = array();
        foreach ($this->headers as $header) {
            $result[$header->getName()] = $header->toArray();
        }

        return $result;
    }
}
<?php

namespace Guzzle\Http\Message\Header;

use Guzzle\Http\Message\Header;

/**
 * Default header factory implementation
 */
class HeaderFactory implements HeaderFactoryInterface
{
    /** @var array */
    protected $mapping = array(
        'cache-control' => 'Guzzle\Http\Message\Header\CacheControl',
        'link'          => 'Guzzle\Http\Message\Header\Link',
    );

    public function createHeader($header, $value = null)
    {
        $lowercase = strtolower($header);

        return isset($this->mapping[$lowercase])
            ? new $this->mapping[$lowercase]($header, $value)
            : new Header($header, $value);
    }
}
<?php

namespace Guzzle\Http\Message\Header;

/**
 * Interface for creating headers
 */
interface HeaderFactoryInterface
{
    /**
     * Create a header from a header name and a single value
     *
     * @param string $header Name of the header to create
     * @param string $value  Value to set on the header
     *
     * @return HeaderInterface
     */
    public function createHeader($header, $value = null);
}
<?php

namespace Guzzle\Http\Message\Header;

use Guzzle\Common\ToArrayInterface;

interface HeaderInterface extends ToArrayInterface, \Countable, \IteratorAggregate
{
    /**
     * Convert the header to a string
     *
     * @return string
     */
    public function __toString();

    /**
     * Add a value to the list of header values
     *
     * @param string $value Value to add to the header
     *
     * @return self
     */
    public function add($value);

    /**
     * Get the name of the header
     *
     * @return string
     */
    public function getName();

    /**
     * Change the name of the header
     *
     * @param string $name Name to change to
     *
     * @return self
     */
    public function setName($name);

    /**
     * Change the glue used to implode the values
     *
     * @param string $glue Glue used to implode multiple values
     *
     * @return self
     */
    public function setGlue($glue);

    /**
     * Get the glue used to implode multiple values into a string
     *
     * @return string
     */
    public function getGlue();

    /**
     * Check if the collection of headers has a particular value
     *
     * @param string $searchValue Value to search for
     *
     * @return bool
     */
    public function hasValue($searchValue);

    /**
     * Remove a specific value from the header
     *
     * @param string $searchValue Value to remove
     *
     * @return self
     */
    public function removeValue($searchValue);

    /**
     * Parse a header containing ";" separated data into an array of associative arrays representing the header
     * key value pair data of the header. When a parameter does not contain a value, but just contains a key, this
     * function will inject a key with a '' string value.
     *
     * @return array
     */
    public function parseParams();
}
<?php

namespace Guzzle\Http\Message\Header;

use Guzzle\Http\Message\Header;

/**
 * Provides helpful functionality for link headers
 */
class Link extends Header
{
    /**
     * Add a link to the header
     *
     * @param string $url    Link URL
     * @param string $rel    Link rel
     * @param array  $params Other link parameters
     *
     * @return self
     */
    public function addLink($url, $rel, array $params = array())
    {
        $values = array("<{$url}>", "rel=\"{$rel}\"");

        foreach ($params as $k => $v) {
            $values[] = "{$k}=\"{$v}\"";
        }

        return $this->add(implode('; ', $values));
    }

    /**
     * Check if a specific link exists for a given rel attribute
     *
     * @param string $rel rel value
     *
     * @return bool
     */
    public function hasLink($rel)
    {
        return $this->getLink($rel) !== null;
    }

    /**
     * Get a specific link for a given rel attribute
     *
     * @param string $rel Rel value
     *
     * @return array|null
     */
    public function getLink($rel)
    {
        foreach ($this->getLinks() as $link) {
            if (isset($link['rel']) && $link['rel'] == $rel) {
                return $link;
            }
        }

        return null;
    }

    /**
     * Get an associative array of links
     *
     * For example:
     * Link: <http:/.../front.jpeg>; rel=front; type="image/jpeg", <http://.../back.jpeg>; rel=back; type="image/jpeg"
     *
     * <code>
     * var_export($response->getLinks());
     * array(
     *     array(
     *         'url' => 'http:/.../front.jpeg',
     *         'rel' => 'back',
     *         'type' => 'image/jpeg',
     *     )
     * )
     * </code>
     *
     * @return array
     */
    public function getLinks()
    {
        $links = $this->parseParams();

        foreach ($links as &$link) {
            $key = key($link);
            unset($link[$key]);
            $link['url'] = trim($key, '<> ');
        }

        return $links;
    }
}
<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Version;
use Guzzle\Http\Message\Header\HeaderInterface;

/**
 * Represents a header and all of the values stored by that header
 */
class Header implements HeaderInterface
{
    protected $values = array();
    protected $header;
    protected $glue;

    /**
     * @param string       $header Name of the header
     * @param array|string $values Values of the header as an array or a scalar
     * @param string       $glue   Glue used to combine multiple values into a string
     */
    public function __construct($header, $values = array(), $glue = ',')
    {
        $this->header = trim($header);
        $this->glue = $glue;

        foreach ((array) $values as $value) {
            foreach ((array) $value as $v) {
                $this->values[] = $v;
            }
        }
    }

    public function __toString()
    {
        return implode($this->glue . ' ', $this->toArray());
    }

    public function add($value)
    {
        $this->values[] = $value;

        return $this;
    }

    public function getName()
    {
        return $this->header;
    }

    public function setName($name)
    {
        $this->header = $name;

        return $this;
    }

    public function setGlue($glue)
    {
        $this->glue = $glue;

        return $this;
    }

    public function getGlue()
    {
        return $this->glue;
    }

    /**
     * Normalize the header to be a single header with an array of values.
     *
     * If any values of the header contains the glue string value (e.g. ","), then the value will be exploded into
     * multiple entries in the header.
     *
     * @return self
     */
    public function normalize()
    {
        $values = $this->toArray();

        for ($i = 0, $total = count($values); $i < $total; $i++) {
            if (strpos($values[$i], $this->glue) !== false) {
                // Explode on glue when the glue is not inside of a comma
                foreach (preg_split('/' . preg_quote($this->glue) . '(?=([^"]*"[^"]*")*[^"]*$)/', $values[$i]) as $v) {
                    $values[] = trim($v);
                }
                unset($values[$i]);
            }
        }

        $this->values = array_values($values);

        return $this;
    }

    public function hasValue($searchValue)
    {
        return in_array($searchValue, $this->toArray());
    }

    public function removeValue($searchValue)
    {
        $this->values = array_values(array_filter($this->values, function ($value) use ($searchValue) {
            return $value != $searchValue;
        }));

        return $this;
    }

    public function toArray()
    {
        return $this->values;
    }

    public function count()
    {
        return count($this->toArray());
    }

    public function getIterator()
    {
        return new \ArrayIterator($this->toArray());
    }

    public function parseParams()
    {
        $params = $matches = array();
        $callback = array($this, 'trimHeader');

        // Normalize the header into a single array and iterate over all values
        foreach ($this->normalize()->toArray() as $val) {
            $part = array();
            foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
                if (!preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) {
                    continue;
                }
                $pieces = array_map($callback, $matches[0]);
                $part[$pieces[0]] = isset($pieces[1]) ? $pieces[1] : '';
            }
            if ($part) {
                $params[] = $part;
            }
        }

        return $params;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function hasExactHeader($header)
    {
        Version::warn(__METHOD__ . ' is deprecated');
        return $this->header == $header;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function raw()
    {
        Version::warn(__METHOD__ . ' is deprecated. Use toArray()');
        return $this->toArray();
    }

    /**
     * Trim a header by removing excess spaces and wrapping quotes
     *
     * @param $str
     *
     * @return string
     */
    protected function trimHeader($str)
    {
        static $trimmed = "\"'  \n\t";

        return trim($str, $trimmed);
    }
}
<?php

namespace Guzzle\Http\Message;

/**
 * Request and response message interface
 */
interface MessageInterface
{
    /**
     * Get application and plugin specific parameters set on the message.
     *
     * @return \Guzzle\Common\Collection
     */
    public function getParams();

    /**
     * Add a header to an existing collection of headers.
     *
     * @param string $header Header name to add
     * @param string $value  Value of the header
     *
     * @return self
     */
    public function addHeader($header, $value);

    /**
     * Add and merge in an array of HTTP headers.
     *
     * @param array $headers Associative array of header data.
     *
     * @return self
     */
    public function addHeaders(array $headers);

    /**
     * Retrieve an HTTP header by name. Performs a case-insensitive search of all headers.
     *
     * @param string $header Header to retrieve.
     *
     * @return Header|null
     */
    public function getHeader($header);

    /**
     * Get all headers as a collection
     *
     * @return \Guzzle\Http\Message\Header\HeaderCollection
     */
    public function getHeaders();

    /**
     * Check if the specified header is present.
     *
     * @param string $header The header to check.
     *
     * @return bool
     */
    public function hasHeader($header);

    /**
     * Remove a specific HTTP header.
     *
     * @param string $header HTTP header to remove.
     *
     * @return self
     */
    public function removeHeader($header);

    /**
     * Set an HTTP header and overwrite any existing value for the header
     *
     * @param string $header Name of the header to set.
     * @param mixed  $value  Value to set.
     *
     * @return self
     */
    public function setHeader($header, $value);

    /**
     * Overwrite all HTTP headers with the supplied array of headers
     *
     * @param array $headers Associative array of header data.
     *
     * @return self
     */
    public function setHeaders(array $headers);

    /**
     * Get an array of message header lines (e.g. ["Host: example.com", ...])
     *
     * @return array
     */
    public function getHeaderLines();

    /**
     * Get the raw message headers as a string
     *
     * @return string
     */
    public function getRawHeaders();
}
<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Version;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Mimetypes;

/**
 * POST file upload
 */
class PostFile implements PostFileInterface
{
    protected $fieldName;
    protected $contentType;
    protected $filename;
    protected $postname;

    /**
     * @param string $fieldName   Name of the field
     * @param string $filename    Local path to the file
     * @param string $postname    Remote post file name
     * @param string $contentType Content-Type of the upload
     */
    public function __construct($fieldName, $filename, $contentType = null, $postname = null)
    {
        $this->fieldName = $fieldName;
        $this->setFilename($filename);
        $this->postname = $postname ? $postname : basename($filename);
        $this->contentType = $contentType ?: $this->guessContentType();
    }

    public function setFieldName($name)
    {
        $this->fieldName = $name;

        return $this;
    }

    public function getFieldName()
    {
        return $this->fieldName;
    }

    public function setFilename($filename)
    {
        // Remove leading @ symbol
        if (strpos($filename, '@') === 0) {
            $filename = substr($filename, 1);
        }

        if (!is_readable($filename)) {
            throw new InvalidArgumentException("Unable to open {$filename} for reading");
        }

        $this->filename = $filename;

        return $this;
    }

    public function setPostname($postname)
    {
        $this->postname = $postname;

        return $this;
    }

    public function getFilename()
    {
        return $this->filename;
    }

    public function getPostname()
    {
        return $this->postname;
    }

    public function setContentType($type)
    {
        $this->contentType = $type;

        return $this;
    }

    public function getContentType()
    {
        return $this->contentType;
    }

    public function getCurlValue()
    {
        // PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax
        // See: https://wiki.php.net/rfc/curl-file-upload
        if (function_exists('curl_file_create')) {
            return curl_file_create($this->filename, $this->contentType, $this->postname);
        }

        // Use the old style if using an older version of PHP
        $value = "@{$this->filename};filename=" . $this->postname;
        if ($this->contentType) {
            $value .= ';type=' . $this->contentType;
        }

        return $value;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function getCurlString()
    {
        Version::warn(__METHOD__ . ' is deprecated. Use getCurlValue()');
        return $this->getCurlValue();
    }

    /**
     * Determine the Content-Type of the file
     */
    protected function guessContentType()
    {
        return Mimetypes::getInstance()->fromFilename($this->filename) ?: 'application/octet-stream';
    }
}
<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * POST file upload
 */
interface PostFileInterface
{
    /**
     * Set the name of the field
     *
     * @param string $name Field name
     *
     * @return self
     */
    public function setFieldName($name);

    /**
     * Get the name of the field
     *
     * @return string
     */
    public function getFieldName();

    /**
     * Set the path to the file
     *
     * @param string $path Full path to the file
     *
     * @return self
     * @throws InvalidArgumentException if the file cannot be read
     */
    public function setFilename($path);

    /**
     * Set the post name of the file
     *
     * @param string $name The new name of the file
     *
     * @return self
     */
    public function setPostname($name);

    /**
     * Get the full path to the file
     *
     * @return string
     */
    public function getFilename();

    /**
     * Get the post name of the file
     *
     * @return string
     */
    public function getPostname();

    /**
     * Set the Content-Type of the file
     *
     * @param string $type Content type
     *
     * @return self
     */
    public function setContentType($type);

    /**
     * Get the Content-Type of the file
     *
     * @return string
     */
    public function getContentType();

    /**
     * Get a cURL ready string or CurlFile object for the upload
     *
     * @return string
     */
    public function getCurlValue();
}
<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Version;
use Guzzle\Common\Event;
use Guzzle\Common\Collection;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Exception\RequestException;
use Guzzle\Http\Exception\BadResponseException;
use Guzzle\Http\ClientInterface;
use Guzzle\Http\EntityBody;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\Message\Header\HeaderInterface;
use Guzzle\Http\Url;
use Guzzle\Parser\ParserRegistry;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * HTTP request class to send requests
 */
class Request extends AbstractMessage implements RequestInterface
{
    /** @var EventDispatcherInterface */
    protected $eventDispatcher;

    /** @var Url HTTP Url */
    protected $url;

    /** @var string HTTP method (GET, PUT, POST, DELETE, HEAD, OPTIONS, TRACE) */
    protected $method;

    /** @var ClientInterface */
    protected $client;

    /** @var Response Response of the request */
    protected $response;

    /** @var EntityBodyInterface Response body */
    protected $responseBody;

    /** @var string State of the request object */
    protected $state;

    /** @var string Authentication username */
    protected $username;

    /** @var string Auth password */
    protected $password;

    /** @var Collection cURL specific transfer options */
    protected $curlOptions;

    /** @var bool */
    protected $isRedirect = false;

    public static function getAllEvents()
    {
        return array(
            // Called when receiving or uploading data through cURL
            'curl.callback.read', 'curl.callback.write', 'curl.callback.progress',
            // Cloning a request
            'request.clone',
            // About to send the request, sent request, completed transaction
            'request.before_send', 'request.sent', 'request.complete',
            // A request received a successful response
            'request.success',
            // A request received an unsuccessful response
            'request.error',
            // An exception is being thrown because of an unsuccessful response
            'request.exception',
            // Received response status line
            'request.receive.status_line'
        );
    }

    /**
     * @param string           $method  HTTP method
     * @param string|Url       $url     HTTP URL to connect to. The URI scheme, host header, and URI are parsed from the
     *                                  full URL. If query string parameters are present they will be parsed as well.
     * @param array|Collection $headers HTTP headers
     */
    public function __construct($method, $url, $headers = array())
    {
        parent::__construct();
        $this->method = strtoupper($method);
        $this->curlOptions = new Collection();
        $this->setUrl($url);

        if ($headers) {
            // Special handling for multi-value headers
            foreach ($headers as $key => $value) {
                // Deal with collisions with Host and Authorization
                if ($key == 'host' || $key == 'Host') {
                    $this->setHeader($key, $value);
                } elseif ($value instanceof HeaderInterface) {
                    $this->addHeader($key, $value);
                } else {
                    foreach ((array) $value as $v) {
                        $this->addHeader($key, $v);
                    }
                }
            }
        }

        $this->setState(self::STATE_NEW);
    }

    public function __clone()
    {
        if ($this->eventDispatcher) {
            $this->eventDispatcher = clone $this->eventDispatcher;
        }
        $this->curlOptions = clone $this->curlOptions;
        $this->params = clone $this->params;
        $this->url = clone $this->url;
        $this->response = $this->responseBody = null;
        $this->headers = clone $this->headers;

        $this->setState(RequestInterface::STATE_NEW);
        $this->dispatch('request.clone', array('request' => $this));
    }

    /**
     * Get the HTTP request as a string
     *
     * @return string
     */
    public function __toString()
    {
        return $this->getRawHeaders() . "\r\n\r\n";
    }

    /**
     * Default method that will throw exceptions if an unsuccessful response is received.
     *
     * @param Event $event Received
     * @throws BadResponseException if the response is not successful
     */
    public static function onRequestError(Event $event)
    {
        $e = BadResponseException::factory($event['request'], $event['response']);
        $event['request']->setState(self::STATE_ERROR, array('exception' => $e) + $event->toArray());
        throw $e;
    }

    public function setClient(ClientInterface $client)
    {
        $this->client = $client;

        return $this;
    }

    public function getClient()
    {
        return $this->client;
    }

    public function getRawHeaders()
    {
        $protocolVersion = $this->protocolVersion ?: '1.1';

        return trim($this->method . ' ' . $this->getResource()) . ' '
            . strtoupper(str_replace('https', 'http', $this->url->getScheme()))
            . '/' . $protocolVersion . "\r\n" . implode("\r\n", $this->getHeaderLines());
    }

    public function setUrl($url)
    {
        if ($url instanceof Url) {
            $this->url = $url;
        } else {
            $this->url = Url::factory($url);
        }

        // Update the port and host header
        $this->setPort($this->url->getPort());

        if ($this->url->getUsername() || $this->url->getPassword()) {
            $this->setAuth($this->url->getUsername(), $this->url->getPassword());
            // Remove the auth info from the URL
            $this->url->setUsername(null);
            $this->url->setPassword(null);
        }

        return $this;
    }

    public function send()
    {
        if (!$this->client) {
            throw new RuntimeException('A client must be set on the request');
        }

        return $this->client->send($this);
    }

    public function getResponse()
    {
        return $this->response;
    }

    public function getQuery($asString = false)
    {
        return $asString
            ? (string) $this->url->getQuery()
            : $this->url->getQuery();
    }

    public function getMethod()
    {
        return $this->method;
    }

    public function getScheme()
    {
        return $this->url->getScheme();
    }

    public function setScheme($scheme)
    {
        $this->url->setScheme($scheme);

        return $this;
    }

    public function getHost()
    {
        return $this->url->getHost();
    }

    public function setHost($host)
    {
        $this->url->setHost($host);
        $this->setPort($this->url->getPort());

        return $this;
    }

    public function getProtocolVersion()
    {
        return $this->protocolVersion;
    }

    public function setProtocolVersion($protocol)
    {
        $this->protocolVersion = $protocol;

        return $this;
    }

    public function getPath()
    {
        return '/' . ltrim($this->url->getPath(), '/');
    }

    public function setPath($path)
    {
        $this->url->setPath($path);

        return $this;
    }

    public function getPort()
    {
        return $this->url->getPort();
    }

    public function setPort($port)
    {
        $this->url->setPort($port);

        // Include the port in the Host header if it is not the default port for the scheme of the URL
        $scheme = $this->url->getScheme();
        if ($port && (($scheme == 'http' && $port != 80) || ($scheme == 'https' && $port != 443))) {
            $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost() . ':' . $port);
        } else {
            $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost());
        }

        return $this;
    }

    public function getUsername()
    {
        return $this->username;
    }

    public function getPassword()
    {
        return $this->password;
    }

    public function setAuth($user, $password = '', $scheme = CURLAUTH_BASIC)
    {
        static $authMap = array(
            'basic'  => CURLAUTH_BASIC,
            'digest' => CURLAUTH_DIGEST,
            'ntlm'   => CURLAUTH_NTLM,
            'any'    => CURLAUTH_ANY
        );

        // If we got false or null, disable authentication
        if (!$user) {
            $this->password = $this->username = null;
            $this->removeHeader('Authorization');
            $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH);
            return $this;
        }

        if (!is_numeric($scheme)) {
            $scheme = strtolower($scheme);
            if (!isset($authMap[$scheme])) {
                throw new InvalidArgumentException($scheme . ' is not a valid authentication type');
            }
            $scheme = $authMap[$scheme];
        }

        $this->username = $user;
        $this->password = $password;

        // Bypass CURL when using basic auth to promote connection reuse
        if ($scheme == CURLAUTH_BASIC) {
            $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH);
            $this->setHeader('Authorization', 'Basic ' . base64_encode($this->username . ':' . $this->password));
        } else {
            $this->getCurlOptions()
                ->set(CURLOPT_HTTPAUTH, $scheme)
                ->set(CURLOPT_USERPWD, $this->username . ':' . $this->password);
        }

        return $this;
    }

    public function getResource()
    {
        $resource = $this->getPath();
        if ($query = (string) $this->url->getQuery()) {
            $resource .= '?' . $query;
        }

        return $resource;
    }

    public function getUrl($asObject = false)
    {
        return $asObject ? clone $this->url : (string) $this->url;
    }

    public function getState()
    {
        return $this->state;
    }

    public function setState($state, array $context = array())
    {
        $oldState = $this->state;
        $this->state = $state;

        switch ($state) {
            case self::STATE_NEW:
                $this->response = null;
                break;
            case self::STATE_TRANSFER:
                if ($oldState !== $state) {
                    // Fix Content-Length and Transfer-Encoding collisions
                    if ($this->hasHeader('Transfer-Encoding') && $this->hasHeader('Content-Length')) {
                        $this->removeHeader('Transfer-Encoding');
                    }
                    $this->dispatch('request.before_send', array('request' => $this));
                }
                break;
            case self::STATE_COMPLETE:
                if ($oldState !== $state) {
                    $this->processResponse($context);
                    $this->responseBody = null;
                }
                break;
            case self::STATE_ERROR:
                if (isset($context['exception'])) {
                    $this->dispatch('request.exception', array(
                        'request'   => $this,
                        'response'  => isset($context['response']) ? $context['response'] : $this->response,
                        'exception' => isset($context['exception']) ? $context['exception'] : null
                    ));
                }
        }

        return $this->state;
    }

    public function getCurlOptions()
    {
        return $this->curlOptions;
    }

    public function startResponse(Response $response)
    {
        $this->state = self::STATE_TRANSFER;
        $response->setEffectiveUrl((string) $this->getUrl());
        $this->response = $response;

        return $this;
    }

    public function setResponse(Response $response, $queued = false)
    {
        $response->setEffectiveUrl((string) $this->url);

        if ($queued) {
            $ed = $this->getEventDispatcher();
            $ed->addListener('request.before_send', $f = function ($e) use ($response, &$f, $ed) {
                $e['request']->setResponse($response);
                $ed->removeListener('request.before_send', $f);
            }, -9999);
        } else {
            $this->response = $response;
            // If a specific response body is specified, then use it instead of the response's body
            if ($this->responseBody && !$this->responseBody->getCustomData('default') && !$response->isRedirect()) {
                $this->getResponseBody()->write((string) $this->response->getBody());
            } else {
                $this->responseBody = $this->response->getBody();
            }
            $this->setState(self::STATE_COMPLETE);
        }

        return $this;
    }

    public function setResponseBody($body)
    {
        // Attempt to open a file for writing if a string was passed
        if (is_string($body)) {
            // @codeCoverageIgnoreStart
            if (!($body = fopen($body, 'w+'))) {
                throw new InvalidArgumentException('Could not open ' . $body . ' for writing');
            }
            // @codeCoverageIgnoreEnd
        }

        $this->responseBody = EntityBody::factory($body);

        return $this;
    }

    public function getResponseBody()
    {
        if ($this->responseBody === null) {
            $this->responseBody = EntityBody::factory()->setCustomData('default', true);
        }

        return $this->responseBody;
    }

    /**
     * Determine if the response body is repeatable (readable + seekable)
     *
     * @return bool
     * @deprecated Use getResponseBody()->isSeekable()
     * @codeCoverageIgnore
     */
    public function isResponseBodyRepeatable()
    {
        Version::warn(__METHOD__ . ' is deprecated. Use $request->getResponseBody()->isRepeatable()');
        return !$this->responseBody ? true : $this->responseBody->isRepeatable();
    }

    public function getCookies()
    {
        if ($cookie = $this->getHeader('Cookie')) {
            $data = ParserRegistry::getInstance()->getParser('cookie')->parseCookie($cookie);
            return $data['cookies'];
        }

        return array();
    }

    public function getCookie($name)
    {
        $cookies = $this->getCookies();

        return isset($cookies[$name]) ? $cookies[$name] : null;
    }

    public function addCookie($name, $value)
    {
        if (!$this->hasHeader('Cookie')) {
            $this->setHeader('Cookie', "{$name}={$value}");
        } else {
            $this->getHeader('Cookie')->add("{$name}={$value}");
        }

        // Always use semicolons to separate multiple cookie headers
        $this->getHeader('Cookie')->setGlue(';');

        return $this;
    }

    public function removeCookie($name)
    {
        if ($cookie = $this->getHeader('Cookie')) {
            foreach ($cookie as $cookieValue) {
                if (strpos($cookieValue, $name . '=') === 0) {
                    $cookie->removeValue($cookieValue);
                }
            }
        }

        return $this;
    }

    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
    {
        $this->eventDispatcher = $eventDispatcher;
        $this->eventDispatcher->addListener('request.error', array(__CLASS__, 'onRequestError'), -255);

        return $this;
    }

    public function getEventDispatcher()
    {
        if (!$this->eventDispatcher) {
            $this->setEventDispatcher(new EventDispatcher());
        }

        return $this->eventDispatcher;
    }

    public function dispatch($eventName, array $context = array())
    {
        $context['request'] = $this;

        return $this->getEventDispatcher()->dispatch($eventName, new Event($context));
    }

    public function addSubscriber(EventSubscriberInterface $subscriber)
    {
        $this->getEventDispatcher()->addSubscriber($subscriber);

        return $this;
    }

    /**
     * Get an array containing the request and response for event notifications
     *
     * @return array
     */
    protected function getEventArray()
    {
        return array(
            'request'  => $this,
            'response' => $this->response
        );
    }

    /**
     * Process a received response
     *
     * @param array $context Contextual information
     * @throws RequestException|BadResponseException on unsuccessful responses
     */
    protected function processResponse(array $context = array())
    {
        if (!$this->response) {
            // If no response, then processResponse shouldn't have been called
            $e = new RequestException('Error completing request');
            $e->setRequest($this);
            throw $e;
        }

        $this->state = self::STATE_COMPLETE;

        // A request was sent, but we don't know if we'll send more or if the final response will be successful
        $this->dispatch('request.sent', $this->getEventArray() + $context);

        // Some response processors will remove the response or reset the state (example: ExponentialBackoffPlugin)
        if ($this->state == RequestInterface::STATE_COMPLETE) {

            // The request completed, so the HTTP transaction is complete
            $this->dispatch('request.complete', $this->getEventArray());

            // If the response is bad, allow listeners to modify it or throw exceptions. You can change the response by
            // modifying the Event object in your listeners or calling setResponse() on the request
            if ($this->response->isError()) {
                $event = new Event($this->getEventArray());
                $this->getEventDispatcher()->dispatch('request.error', $event);
                // Allow events of request.error to quietly change the response
                if ($event['response'] !== $this->response) {
                    $this->response = $event['response'];
                }
            }

            // If a successful response was received, dispatch an event
            if ($this->response->isSuccessful()) {
                $this->dispatch('request.success', $this->getEventArray());
            }
        }
    }

    /**
     * @deprecated Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy
     * @codeCoverageIgnore
     */
    public function canCache()
    {
        Version::warn(__METHOD__ . ' is deprecated. Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy.');
        if (class_exists('Guzzle\Plugin\Cache\DefaultCanCacheStrategy')) {
            $canCache = new \Guzzle\Plugin\Cache\DefaultCanCacheStrategy();
            return $canCache->canCacheRequest($this);
        } else {
            return false;
        }
    }

    /**
     * @deprecated Use the history plugin (not emitting a warning as this is built-into the RedirectPlugin for now)
     * @codeCoverageIgnore
     */
    public function setIsRedirect($isRedirect)
    {
        $this->isRedirect = $isRedirect;

        return $this;
    }

    /**
     * @deprecated Use the history plugin
     * @codeCoverageIgnore
     */
    public function isRedirect()
    {
        Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin to track this.');
        return $this->isRedirect;
    }
}
<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Collection;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\RedirectPlugin;
use Guzzle\Http\Url;
use Guzzle\Parser\ParserRegistry;

/**
 * Default HTTP request factory used to create the default {@see Request} and {@see EntityEnclosingRequest} objects.
 */
class RequestFactory implements RequestFactoryInterface
{
    /** @var RequestFactory Singleton instance of the default request factory */
    protected static $instance;

    /** @var array Hash of methods available to the class (provides fast isset() lookups) */
    protected $methods;

    /** @var string Class to instantiate for requests with no body */
    protected $requestClass = 'Guzzle\\Http\\Message\\Request';

    /** @var string Class to instantiate for requests with a body */
    protected $entityEnclosingRequestClass = 'Guzzle\\Http\\Message\\EntityEnclosingRequest';

    /**
     * Get a cached instance of the default request factory
     *
     * @return RequestFactory
     */
    public static function getInstance()
    {
        // @codeCoverageIgnoreStart
        if (!static::$instance) {
            static::$instance = new static();
        }
        // @codeCoverageIgnoreEnd

        return static::$instance;
    }

    public function __construct()
    {
        $this->methods = array_flip(get_class_methods(__CLASS__));
    }

    public function fromMessage($message)
    {
        $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($message);

        if (!$parsed) {
            return false;
        }

        $request = $this->fromParts($parsed['method'], $parsed['request_url'],
            $parsed['headers'], $parsed['body'], $parsed['protocol'],
            $parsed['version']);

        // EntityEnclosingRequest adds an "Expect: 100-Continue" header when using a raw request body for PUT or POST
        // requests. This factory method should accurately reflect the message, so here we are removing the Expect
        // header if one was not supplied in the message.
        if (!isset($parsed['headers']['Expect']) && !isset($parsed['headers']['expect'])) {
            $request->removeHeader('Expect');
        }

        return $request;
    }

    public function fromParts(
        $method,
        array $urlParts,
        $headers = null,
        $body = null,
        $protocol = 'HTTP',
        $protocolVersion = '1.1'
    ) {
        return $this->create($method, Url::buildUrl($urlParts), $headers, $body)
                    ->setProtocolVersion($protocolVersion);
    }

    public function create($method, $url, $headers = null, $body = null, array $options = array())
    {
        $method = strtoupper($method);

        if ($method == 'GET' || $method == 'HEAD' || $method == 'TRACE') {
            // Handle non-entity-enclosing request methods
            $request = new $this->requestClass($method, $url, $headers);
            if ($body) {
                // The body is where the response body will be stored
                $type = gettype($body);
                if ($type == 'string' || $type == 'resource' || $type == 'object') {
                    $request->setResponseBody($body);
                }
            }
        } else {
            // Create an entity enclosing request by default
            $request = new $this->entityEnclosingRequestClass($method, $url, $headers);
            if ($body || $body === '0') {
                // Add POST fields and files to an entity enclosing request if an array is used
                if (is_array($body) || $body instanceof Collection) {
                    // Normalize PHP style cURL uploads with a leading '@' symbol
                    foreach ($body as $key => $value) {
                        if (is_string($value) && substr($value, 0, 1) == '@') {
                            $request->addPostFile($key, $value);
                            unset($body[$key]);
                        }
                    }
                    // Add the fields if they are still present and not all files
                    $request->addPostFields($body);
                } else {
                    // Add a raw entity body body to the request
                    $request->setBody($body, (string) $request->getHeader('Content-Type'));
                    if ((string) $request->getHeader('Transfer-Encoding') == 'chunked') {
                        $request->removeHeader('Content-Length');
                    }
                }
            }
        }

        if ($options) {
            $this->applyOptions($request, $options);
        }

        return $request;
    }

    /**
     * Clone a request while changing the method. Emulates the behavior of
     * {@see Guzzle\Http\Message\Request::clone}, but can change the HTTP method.
     *
     * @param RequestInterface $request Request to clone
     * @param string           $method  Method to set
     *
     * @return RequestInterface
     */
    public function cloneRequestWithMethod(RequestInterface $request, $method)
    {
        // Create the request with the same client if possible
        if ($request->getClient()) {
            $cloned = $request->getClient()->createRequest($method, $request->getUrl(), $request->getHeaders());
        } else {
            $cloned = $this->create($method, $request->getUrl(), $request->getHeaders());
        }

        $cloned->getCurlOptions()->replace($request->getCurlOptions()->toArray());
        $cloned->setEventDispatcher(clone $request->getEventDispatcher());
        // Ensure that that the Content-Length header is not copied if changing to GET or HEAD
        if (!($cloned instanceof EntityEnclosingRequestInterface)) {
            $cloned->removeHeader('Content-Length');
        } elseif ($request instanceof EntityEnclosingRequestInterface) {
            $cloned->setBody($request->getBody());
        }
        $cloned->getParams()->replace($request->getParams()->toArray());
        $cloned->dispatch('request.clone', array('request' => $cloned));

        return $cloned;
    }

    public function applyOptions(RequestInterface $request, array $options = array(), $flags = self::OPTIONS_NONE)
    {
        // Iterate over each key value pair and attempt to apply a config using function visitors
        foreach ($options as $key => $value) {
            $method = "visit_{$key}";
            if (isset($this->methods[$method])) {
                $this->{$method}($request, $value, $flags);
            }
        }
    }

    protected function visit_headers(RequestInterface $request, $value, $flags)
    {
        if (!is_array($value)) {
            throw new InvalidArgumentException('headers value must be an array');
        }

        if ($flags & self::OPTIONS_AS_DEFAULTS) {
            // Merge headers in but do not overwrite existing values
            foreach ($value as $key => $header) {
                if (!$request->hasHeader($key)) {
                    $request->setHeader($key, $header);
                }
            }
        } else {
            $request->addHeaders($value);
        }
    }

    protected function visit_body(RequestInterface $request, $value, $flags)
    {
        if ($request instanceof EntityEnclosingRequestInterface) {
            $request->setBody($value);
        } else {
            throw new InvalidArgumentException('Attempting to set a body on a non-entity-enclosing request');
        }
    }

    protected function visit_allow_redirects(RequestInterface $request, $value, $flags)
    {
        if ($value === false) {
            $request->getParams()->set(RedirectPlugin::DISABLE, true);
        }
    }

    protected function visit_auth(RequestInterface $request, $value, $flags)
    {
        if (!is_array($value)) {
            throw new InvalidArgumentException('auth value must be an array');
        }

        $request->setAuth($value[0], isset($value[1]) ? $value[1] : null, isset($value[2]) ? $value[2] : 'basic');
    }

    protected function visit_query(RequestInterface $request, $value, $flags)
    {
        if (!is_array($value)) {
            throw new InvalidArgumentException('query value must be an array');
        }

        if ($flags & self::OPTIONS_AS_DEFAULTS) {
            // Merge query string values in but do not overwrite existing values
            $query = $request->getQuery();
            $query->overwriteWith(array_diff_key($value, $query->toArray()));
        } else {
            $request->getQuery()->overwriteWith($value);
        }
    }

    protected function visit_cookies(RequestInterface $request, $value, $flags)
    {
        if (!is_array($value)) {
            throw new InvalidArgumentException('cookies value must be an array');
        }

        foreach ($value as $name => $v) {
            $request->addCookie($name, $v);
        }
    }

    protected function visit_events(RequestInterface $request, $value, $flags)
    {
        if (!is_array($value)) {
            throw new InvalidArgumentException('events value must be an array');
        }

        foreach ($value as $name => $method) {
            if (is_array($method)) {
                $request->getEventDispatcher()->addListener($name, $method[0], $method[1]);
            } else {
                $request->getEventDispatcher()->addListener($name, $method);
            }
        }
    }

    protected function visit_plugins(RequestInterface $request, $value, $flags)
    {
        if (!is_array($value)) {
            throw new InvalidArgumentException('plugins value must be an array');
        }

        foreach ($value as $plugin) {
            $request->addSubscriber($plugin);
        }
    }

    protected function visit_exceptions(RequestInterface $request, $value, $flags)
    {
        if ($value === false || $value === 0) {
            $dispatcher = $request->getEventDispatcher();
            foreach ($dispatcher->getListeners('request.error') as $listener) {
                if (is_array($listener) && $listener[0] == 'Guzzle\Http\Message\Request' && $listener[1] = 'onRequestError') {
                    $dispatcher->removeListener('request.error', $listener);
                    break;
                }
            }
        }
    }

    protected function visit_save_to(RequestInterface $request, $value, $flags)
    {
        $request->setResponseBody($value);
    }

    protected function visit_params(RequestInterface $request, $value, $flags)
    {
        if (!is_array($value)) {
            throw new InvalidArgumentException('params value must be an array');
        }

        $request->getParams()->overwriteWith($value);
    }

    protected function visit_timeout(RequestInterface $request, $value, $flags)
    {
        if (defined('CURLOPT_TIMEOUT_MS')) {
            $request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, $value * 1000);
        } else {
            $request->getCurlOptions()->set(CURLOPT_TIMEOUT, $value);
        }
    }

    protected function visit_connect_timeout(RequestInterface $request, $value, $flags)
    {
        if (defined('CURLOPT_CONNECTTIMEOUT_MS')) {
            $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, $value * 1000);
        } else {
            $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT, $value);
        }
    }

    protected function visit_debug(RequestInterface $request, $value, $flags)
    {
        if ($value) {
            $request->getCurlOptions()->set(CURLOPT_VERBOSE, true);
        }
    }

    protected function visit_verify(RequestInterface $request, $value, $flags)
    {
        $curl = $request->getCurlOptions();
        if ($value === true || is_string($value)) {
            $curl[CURLOPT_SSL_VERIFYHOST] = 2;
            $curl[CURLOPT_SSL_VERIFYPEER] = true;
            if ($value !== true) {
                $curl[CURLOPT_CAINFO] = $value;
            }
        } elseif ($value === false) {
            unset($curl[CURLOPT_CAINFO]);
            $curl[CURLOPT_SSL_VERIFYHOST] = 0;
            $curl[CURLOPT_SSL_VERIFYPEER] = false;
        }
    }

    protected function visit_proxy(RequestInterface $request, $value, $flags)
    {
        $request->getCurlOptions()->set(CURLOPT_PROXY, $value, $flags);
    }

    protected function visit_cert(RequestInterface $request, $value, $flags)
    {
        if (is_array($value)) {
            $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value[0]);
            $request->getCurlOptions()->set(CURLOPT_SSLCERTPASSWD, $value[1]);
        } else {
            $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value);
        }
    }

    protected function visit_ssl_key(RequestInterface $request, $value, $flags)
    {
        if (is_array($value)) {
            $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value[0]);
            $request->getCurlOptions()->set(CURLOPT_SSLKEYPASSWD, $value[1]);
        } else {
            $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value);
        }
    }
}
<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Collection;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\Url;

/**
 * Request factory used to create HTTP requests
 */
interface RequestFactoryInterface
{
    const OPTIONS_NONE = 0;
    const OPTIONS_AS_DEFAULTS = 1;

    /**
     * Create a new request based on an HTTP message
     *
     * @param string $message HTTP message as a string
     *
     * @return RequestInterface
     */
    public function fromMessage($message);

    /**
     * Create a request from URL parts as returned from parse_url()
     *
     * @param string $method HTTP method (GET, POST, PUT, HEAD, DELETE, etc)
     *
     * @param array $urlParts URL parts containing the same keys as parse_url()
     *     - scheme: e.g. http
     *     - host:   e.g. www.guzzle-project.com
     *     - port:   e.g. 80
     *     - user:   e.g. michael
     *     - pass:   e.g. rocks
     *     - path:   e.g. / OR /index.html
     *     - query:  after the question mark ?
     * @param array|Collection                          $headers         HTTP headers
     * @param string|resource|array|EntityBodyInterface $body            Body to send in the request
     * @param string                                    $protocol        Protocol (HTTP, SPYDY, etc)
     * @param string                                    $protocolVersion 1.0, 1.1, etc
     *
     * @return RequestInterface
     */
    public function fromParts(
        $method,
        array $urlParts,
        $headers = null,
        $body = null,
        $protocol = 'HTTP',
        $protocolVersion = '1.1'
    );

    /**
     * Create a new request based on the HTTP method
     *
     * @param string                                    $method  HTTP method (GET, POST, PUT, PATCH, HEAD, DELETE, ...)
     * @param string|Url                                $url     HTTP URL to connect to
     * @param array|Collection                          $headers HTTP headers
     * @param string|resource|array|EntityBodyInterface $body    Body to send in the request
     * @param array                                     $options Array of options to apply to the request
     *
     * @return RequestInterface
     */
    public function create($method, $url, $headers = null, $body = null, array $options = array());

    /**
     * Apply an associative array of options to the request
     *
     * @param RequestInterface $request Request to update
     * @param array            $options Options to use with the request. Available options are:
     *        "headers": Associative array of headers
     *        "query": Associative array of query string values to add to the request
     *        "body": Body of a request, including an EntityBody, string, or array when sending POST requests.
     *        "auth": Array of HTTP authentication parameters to use with the request. The array must contain the
     *            username in index [0], the password in index [2], and can optionally contain the authentication type
     *            in index [3]. The authentication types are: "Basic", "Digest", "NTLM", "Any" (defaults to "Basic").
     *        "cookies": Associative array of cookies
     *        "allow_redirects": Set to false to disable redirects
     *        "save_to": String, fopen resource, or EntityBody object used to store the body of the response
     *        "events": Associative array mapping event names to a closure or array of (priority, closure)
     *        "plugins": Array of plugins to add to the request
     *        "exceptions": Set to false to disable throwing exceptions on an HTTP level error (e.g. 404, 500, etc)
     *        "params": Set custom request data parameters on a request. (Note: these are not query string parameters)
     *        "timeout": Float describing the timeout of the request in seconds
     *        "connect_timeout": Float describing the number of seconds to wait while trying to connect. Use 0 to wait
     *            indefinitely.
     *        "verify": Set to true to enable SSL cert validation (the default), false to disable, or supply the path
     *            to a CA bundle to enable verification using a custom certificate.
     *        "cert": Set to a string to specify the path to a file containing a PEM formatted certificate. If a
     *            password is required, then set an array containing the path to the PEM file followed by the the
     *            password required for the certificate.
     *        "ssl_key": Specify the path to a file containing a private SSL key in PEM format. If a password is
     *            required, then set an array containing the path to the SSL key followed by the password required for
     *            the certificate.
     *        "proxy": Specify an HTTP proxy (e.g. "http://username:password@192.168.16.1:10")
     *        "debug": Set to true to display all data sent over the wire
     * @param int $flags Bitwise flags to apply when applying the options to the request. Defaults to no special
     *                   options. `1` (OPTIONS_AS_DEFAULTS): When specified, options will only update a request when
     *                   the value does not already exist on the request. This is only supported by "query" and
     *                   "headers". Other bitwise options may be added in the future.
     */
    public function applyOptions(RequestInterface $request, array $options = array(), $flags = self::OPTIONS_NONE);
}
<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Collection;
use Guzzle\Common\HasDispatcherInterface;
use Guzzle\Http\Exception\RequestException;
use Guzzle\Http\ClientInterface;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\Url;
use Guzzle\Http\QueryString;

/**
 * Generic HTTP request interface
 */
interface RequestInterface extends MessageInterface, HasDispatcherInterface
{
    const STATE_NEW = 'new';
    const STATE_COMPLETE = 'complete';
    const STATE_TRANSFER = 'transfer';
    const STATE_ERROR = 'error';

    const GET = 'GET';
    const PUT = 'PUT';
    const POST = 'POST';
    const DELETE = 'DELETE';
    const HEAD = 'HEAD';
    const CONNECT = 'CONNECT';
    const OPTIONS = 'OPTIONS';
    const TRACE = 'TRACE';
    const PATCH = 'PATCH';

    /**
     * @return string
     */
    public function __toString();

    /**
     * Send the request
     *
     * @return Response
     * @throws RequestException on a request error
     */
    public function send();

    /**
     * Set the client used to transport the request
     *
     * @param ClientInterface $client
     *
     * @return self
     */
    public function setClient(ClientInterface $client);

    /**
     * Get the client used to transport the request
     *
     * @return ClientInterface $client
     */
    public function getClient();

    /**
     * Set the URL of the request
     *
     * @param string $url|Url Full URL to set including query string
     *
     * @return self
     */
    public function setUrl($url);

    /**
     * Get the full URL of the request (e.g. 'http://www.guzzle-project.com/')
     *
     * @param bool $asObject Set to TRUE to retrieve the URL as a clone of the URL object owned by the request.
     *
     * @return string|Url
     */
    public function getUrl($asObject = false);

    /**
     * Get the resource part of the the request, including the path, query string, and fragment
     *
     * @return string
     */
    public function getResource();

    /**
     * Get the collection of key value pairs that will be used as the query string in the request
     *
     * @return QueryString
     */
    public function getQuery();

    /**
     * Get the HTTP method of the request
     *
     * @return string
     */
    public function getMethod();

    /**
     * Get the URI scheme of the request (http, https, ftp, etc)
     *
     * @return string
     */
    public function getScheme();

    /**
     * Set the URI scheme of the request (http, https, ftp, etc)
     *
     * @param string $scheme Scheme to set
     *
     * @return self
     */
    public function setScheme($scheme);

    /**
     * Get the host of the request
     *
     * @return string
     */
    public function getHost();

    /**
     * Set the host of the request. Including a port in the host will modify the port of the request.
     *
     * @param string $host Host to set (e.g. www.yahoo.com, www.yahoo.com:80)
     *
     * @return self
     */
    public function setHost($host);

    /**
     * Get the path of the request (e.g. '/', '/index.html')
     *
     * @return string
     */
    public function getPath();

    /**
     * Set the path of the request (e.g. '/', '/index.html')
     *
     * @param string|array $path Path to set or array of segments to implode
     *
     * @return self
     */
    public function setPath($path);

    /**
     * Get the port that the request will be sent on if it has been set
     *
     * @return int|null
     */
    public function getPort();

    /**
     * Set the port that the request will be sent on
     *
     * @param int $port Port number to set
     *
     * @return self
     */
    public function setPort($port);

    /**
     * Get the username to pass in the URL if set
     *
     * @return string|null
     */
    public function getUsername();

    /**
     * Get the password to pass in the URL if set
     *
     * @return string|null
     */
    public function getPassword();

    /**
     * Set HTTP authorization parameters
     *
     * @param string|bool $user     User name or false disable authentication
     * @param string      $password Password
     * @param string      $scheme   Authentication scheme ('Basic', 'Digest', or a CURLAUTH_* constant (deprecated))
     *
     * @return self
     * @link http://www.ietf.org/rfc/rfc2617.txt
     * @link http://php.net/manual/en/function.curl-setopt.php See the available options for CURLOPT_HTTPAUTH
     * @throws RequestException
     */
    public function setAuth($user, $password = '', $scheme = 'Basic');

    /**
     * Get the HTTP protocol version of the request
     *
     * @return string
     */
    public function getProtocolVersion();

    /**
     * Set the HTTP protocol version of the request (e.g. 1.1 or 1.0)
     *
     * @param string $protocol HTTP protocol version to use with the request
     *
     * @return self
     */
    public function setProtocolVersion($protocol);

    /**
     * Get the previously received {@see Response} or NULL if the request has not been sent
     *
     * @return Response|null
     */
    public function getResponse();

    /**
     * Manually set a response for the request.
     *
     * This method is useful for specifying a mock response for the request or setting the response using a cache.
     * Manually setting a response will bypass the actual sending of a request.
     *
     * @param Response $response Response object to set
     * @param bool     $queued   Set to TRUE to keep the request in a state of not having been sent, but queue the
     *                           response for send()
     *
     * @return self Returns a reference to the object.
     */
    public function setResponse(Response $response, $queued = false);

    /**
     * The start of a response has been received for a request and the request is still in progress
     *
     * @param Response $response Response that has been received so far
     *
     * @return self
     */
    public function startResponse(Response $response);

    /**
     * Set the EntityBody that will hold a successful response message's entity body.
     *
     * This method should be invoked when you need to send the response's entity body somewhere other than the normal
     * php://temp buffer. For example, you can send the entity body to a socket, file, or some other custom stream.
     *
     * @param EntityBodyInterface|string|resource $body Response body object. Pass a string to attempt to store the
     *                                                  response body in a local file.
     * @return Request
     */
    public function setResponseBody($body);

    /**
     * Get the EntityBody that will hold the resulting response message's entity body. This response body will only
     * be used for successful responses. Intermediate responses (e.g. redirects) will not use the targeted response
     * body.
     *
     * @return EntityBodyInterface
     */
    public function getResponseBody();

    /**
     * Get the state of the request. One of 'complete', 'transfer', 'new', 'error'
     *
     * @return string
     */
    public function getState();

    /**
     * Set the state of the request
     *
     * @param string $state   State of the request ('complete', 'transfer', 'new', 'error')
     * @param array  $context Contextual information about the state change
     *
     * @return string Returns the current state of the request (which may have changed due to events being fired)
     */
    public function setState($state, array $context = array());

    /**
     * Get the cURL options that will be applied when the cURL handle is created
     *
     * @return Collection
     */
    public function getCurlOptions();

    /**
     * Get an array of Cookies
     *
     * @return array
     */
    public function getCookies();

    /**
     * Get a cookie value by name
     *
     * @param string $name Cookie to retrieve
     *
     * @return null|string
     */
    public function getCookie($name);

    /**
     * Add a Cookie value by name to the Cookie header
     *
     * @param string $name  Name of the cookie to add
     * @param string $value Value to set
     *
     * @return self
     */
    public function addCookie($name, $value);

    /**
     * Remove a specific cookie value by name
     *
     * @param string $name Cookie to remove by name
     *
     * @return self
     */
    public function removeCookie($name);
}
<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Version;
use Guzzle\Common\ToArrayInterface;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\EntityBody;
use Guzzle\Http\Exception\BadResponseException;
use Guzzle\Http\RedirectPlugin;
use Guzzle\Parser\ParserRegistry;

/**
 * Guzzle HTTP response object
 */
class Response extends AbstractMessage implements \Serializable
{
    /**
     * @var array Array of reason phrases and their corresponding status codes
     */
    private static $statusTexts = array(
        100 => 'Continue',
        101 => 'Switching Protocols',
        102 => 'Processing',
        200 => 'OK',
        201 => 'Created',
        202 => 'Accepted',
        203 => 'Non-Authoritative Information',
        204 => 'No Content',
        205 => 'Reset Content',
        206 => 'Partial Content',
        207 => 'Multi-Status',
        208 => 'Already Reported',
        226 => 'IM Used',
        300 => 'Multiple Choices',
        301 => 'Moved Permanently',
        302 => 'Found',
        303 => 'See Other',
        304 => 'Not Modified',
        305 => 'Use Proxy',
        307 => 'Temporary Redirect',
        308 => 'Permanent Redirect',
        400 => 'Bad Request',
        401 => 'Unauthorized',
        402 => 'Payment Required',
        403 => 'Forbidden',
        404 => 'Not Found',
        405 => 'Method Not Allowed',
        406 => 'Not Acceptable',
        407 => 'Proxy Authentication Required',
        408 => 'Request Timeout',
        409 => 'Conflict',
        410 => 'Gone',
        411 => 'Length Required',
        412 => 'Precondition Failed',
        413 => 'Request Entity Too Large',
        414 => 'Request-URI Too Long',
        415 => 'Unsupported Media Type',
        416 => 'Requested Range Not Satisfiable',
        417 => 'Expectation Failed',
        422 => 'Unprocessable Entity',
        423 => 'Locked',
        424 => 'Failed Dependency',
        425 => 'Reserved for WebDAV advanced collections expired proposal',
        426 => 'Upgrade required',
        428 => 'Precondition Required',
        429 => 'Too Many Requests',
        431 => 'Request Header Fields Too Large',
        500 => 'Internal Server Error',
        501 => 'Not Implemented',
        502 => 'Bad Gateway',
        503 => 'Service Unavailable',
        504 => 'Gateway Timeout',
        505 => 'HTTP Version Not Supported',
        506 => 'Variant Also Negotiates (Experimental)',
        507 => 'Insufficient Storage',
        508 => 'Loop Detected',
        510 => 'Not Extended',
        511 => 'Network Authentication Required',
    );

    /** @var EntityBodyInterface The response body */
    protected $body;

    /** @var string The reason phrase of the response (human readable code) */
    protected $reasonPhrase;

    /** @var string The status code of the response */
    protected $statusCode;

    /** @var array Information about the request */
    protected $info = array();

    /** @var string The effective URL that returned this response */
    protected $effectiveUrl;

    /** @var array Cacheable response codes (see RFC 2616:13.4) */
    protected static $cacheResponseCodes = array(200, 203, 206, 300, 301, 410);

    /**
     * Create a new Response based on a raw response message
     *
     * @param string $message Response message
     *
     * @return self|bool Returns false on error
     */
    public static function fromMessage($message)
    {
        $data = ParserRegistry::getInstance()->getParser('message')->parseResponse($message);
        if (!$data) {
            return false;
        }

        $response = new static($data['code'], $data['headers'], $data['body']);
        $response->setProtocol($data['protocol'], $data['version'])
                 ->setStatus($data['code'], $data['reason_phrase']);

        // Set the appropriate Content-Length if the one set is inaccurate (e.g. setting to X)
        $contentLength = (string) $response->getHeader('Content-Length');
        $actualLength = strlen($data['body']);
        if (strlen($data['body']) > 0 && $contentLength != $actualLength) {
            $response->setHeader('Content-Length', $actualLength);
        }

        return $response;
    }

    /**
     * Construct the response
     *
     * @param string                              $statusCode The response status code (e.g. 200, 404, etc)
     * @param ToArrayInterface|array              $headers    The response headers
     * @param string|resource|EntityBodyInterface $body       The body of the response
     *
     * @throws BadResponseException if an invalid response code is given
     */
    public function __construct($statusCode, $headers = null, $body = null)
    {
        parent::__construct();
        $this->setStatus($statusCode);
        $this->body = EntityBody::factory($body !== null ? $body : '');

        if ($headers) {
            if (is_array($headers)) {
                $this->setHeaders($headers);
            } elseif ($headers instanceof ToArrayInterface) {
                $this->setHeaders($headers->toArray());
            } else {
                throw new BadResponseException('Invalid headers argument received');
            }
        }
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return $this->getMessage();
    }

    public function serialize()
    {
        return json_encode(array(
            'status'  => $this->statusCode,
            'body'    => (string) $this->body,
            'headers' => $this->headers->toArray()
        ));
    }

    public function unserialize($serialize)
    {
        $data = json_decode($serialize, true);
        $this->__construct($data['status'], $data['headers'], $data['body']);
    }

    /**
     * Get the response entity body
     *
     * @param bool $asString Set to TRUE to return a string of the body rather than a full body object
     *
     * @return EntityBodyInterface|string
     */
    public function getBody($asString = false)
    {
        return $asString ? (string) $this->body : $this->body;
    }

    /**
     * Set the response entity body
     *
     * @param EntityBodyInterface|string $body Body to set
     *
     * @return self
     */
    public function setBody($body)
    {
        $this->body = EntityBody::factory($body);

        return $this;
    }

    /**
     * Set the protocol and protocol version of the response
     *
     * @param string $protocol Response protocol
     * @param string $version  Protocol version
     *
     * @return self
     */
    public function setProtocol($protocol, $version)
    {
        $this->protocol = $protocol;
        $this->protocolVersion = $version;

        return $this;
    }

    /**
     * Get the protocol used for the response (e.g. HTTP)
     *
     * @return string
     */
    public function getProtocol()
    {
        return $this->protocol;
    }

    /**
     * Get the HTTP protocol version
     *
     * @return string
     */
    public function getProtocolVersion()
    {
        return $this->protocolVersion;
    }

    /**
     * Get a cURL transfer information
     *
     * @param string $key A single statistic to check
     *
     * @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
     *                           is set and not found
     * @link http://www.php.net/manual/en/function.curl-getinfo.php
     */
    public function getInfo($key = null)
    {
        if ($key === null) {
            return $this->info;
        } elseif (array_key_exists($key, $this->info)) {
            return $this->info[$key];
        } else {
            return null;
        }
    }

    /**
     * Set the transfer information
     *
     * @param array $info Array of cURL transfer stats
     *
     * @return self
     */
    public function setInfo(array $info)
    {
        $this->info = $info;

        return $this;
    }

    /**
     * Set the response status
     *
     * @param int    $statusCode   Response status code to set
     * @param string $reasonPhrase Response reason phrase
     *
     * @return self
     * @throws BadResponseException when an invalid response code is received
     */
    public function setStatus($statusCode, $reasonPhrase = '')
    {
        $this->statusCode = (int) $statusCode;

        if (!$reasonPhrase && isset(self::$statusTexts[$this->statusCode])) {
            $this->reasonPhrase = self::$statusTexts[$this->statusCode];
        } else {
            $this->reasonPhrase = $reasonPhrase;
        }

        return $this;
    }

    /**
     * Get the response status code
     *
     * @return integer
     */
    public function getStatusCode()
    {
        return $this->statusCode;
    }

    /**
     * Get the entire response as a string
     *
     * @return string
     */
    public function getMessage()
    {
        $message = $this->getRawHeaders();

        // Only include the body in the message if the size is < 2MB
        $size = $this->body->getSize();
        if ($size < 2097152) {
            $message .= (string) $this->body;
        }

        return $message;
    }

    /**
     * Get the the raw message headers as a string
     *
     * @return string
     */
    public function getRawHeaders()
    {
        $headers = 'HTTP/1.1 ' . $this->statusCode . ' ' . $this->reasonPhrase . "\r\n";
        $lines = $this->getHeaderLines();
        if (!empty($lines)) {
            $headers .= implode("\r\n", $lines) . "\r\n";
        }

        return $headers . "\r\n";
    }

    /**
     * Get the response reason phrase- a human readable version of the numeric
     * status code
     *
     * @return string
     */
    public function getReasonPhrase()
    {
        return $this->reasonPhrase;
    }

    /**
     * Get the Accept-Ranges HTTP header
     *
     * @return string Returns what partial content range types this server supports.
     */
    public function getAcceptRanges()
    {
        return (string) $this->getHeader('Accept-Ranges');
    }

    /**
     * Calculate the age of the response
     *
     * @return integer
     */
    public function calculateAge()
    {
        $age = $this->getHeader('Age');

        if ($age === null && $this->getDate()) {
            $age = time() - strtotime($this->getDate());
        }

        return $age === null ? null : (int) (string) $age;
    }

    /**
     * Get the Age HTTP header
     *
     * @return integer|null Returns the age the object has been in a proxy cache in seconds.
     */
    public function getAge()
    {
        return (string) $this->getHeader('Age');
    }

    /**
     * Get the Allow HTTP header
     *
     * @return string|null Returns valid actions for a specified resource. To be used for a 405 Method not allowed.
     */
    public function getAllow()
    {
        return (string) $this->getHeader('Allow');
    }

    /**
     * Check if an HTTP method is allowed by checking the Allow response header
     *
     * @param string $method Method to check
     *
     * @return bool
     */
    public function isMethodAllowed($method)
    {
        $allow = $this->getHeader('Allow');
        if ($allow) {
            foreach (explode(',', $allow) as $allowable) {
                if (!strcasecmp(trim($allowable), $method)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Get the Cache-Control HTTP header
     *
     * @return string
     */
    public function getCacheControl()
    {
        return (string) $this->getHeader('Cache-Control');
    }

    /**
     * Get the Connection HTTP header
     *
     * @return string
     */
    public function getConnection()
    {
        return (string) $this->getHeader('Connection');
    }

    /**
     * Get the Content-Encoding HTTP header
     *
     * @return string|null
     */
    public function getContentEncoding()
    {
        return (string) $this->getHeader('Content-Encoding');
    }

    /**
     * Get the Content-Language HTTP header
     *
     * @return string|null Returns the language the content is in.
     */
    public function getContentLanguage()
    {
        return (string) $this->getHeader('Content-Language');
    }

    /**
     * Get the Content-Length HTTP header
     *
     * @return integer Returns the length of the response body in bytes
     */
    public function getContentLength()
    {
        return (int) (string) $this->getHeader('Content-Length');
    }

    /**
     * Get the Content-Location HTTP header
     *
     * @return string|null Returns an alternate location for the returned data (e.g /index.htm)
     */
    public function getContentLocation()
    {
        return (string) $this->getHeader('Content-Location');
    }

    /**
     * Get the Content-Disposition HTTP header
     *
     * @return string|null Returns the Content-Disposition header
     */
    public function getContentDisposition()
    {
        return (string) $this->getHeader('Content-Disposition');
    }

    /**
     * Get the Content-MD5 HTTP header
     *
     * @return string|null Returns a Base64-encoded binary MD5 sum of the content of the response.
     */
    public function getContentMd5()
    {
        return (string) $this->getHeader('Content-MD5');
    }

    /**
     * Get the Content-Range HTTP header
     *
     * @return string Returns where in a full body message this partial message belongs (e.g. bytes 21010-47021/47022).
     */
    public function getContentRange()
    {
        return (string) $this->getHeader('Content-Range');
    }

    /**
     * Get the Content-Type HTTP header
     *
     * @return string Returns the mime type of this content.
     */
    public function getContentType()
    {
        return (string) $this->getHeader('Content-Type');
    }

    /**
     * Checks if the Content-Type is of a certain type.  This is useful if the
     * Content-Type header contains charset information and you need to know if
     * the Content-Type matches a particular type.
     *
     * @param string $type Content type to check against
     *
     * @return bool
     */
    public function isContentType($type)
    {
        return stripos($this->getHeader('Content-Type'), $type) !== false;
    }

    /**
     * Get the Date HTTP header
     *
     * @return string|null Returns the date and time that the message was sent.
     */
    public function getDate()
    {
        return (string) $this->getHeader('Date');
    }

    /**
     * Get the ETag HTTP header
     *
     * @return string|null Returns an identifier for a specific version of a resource, often a Message digest.
     */
    public function getEtag()
    {
        return (string) $this->getHeader('ETag');
    }

    /**
     * Get the Expires HTTP header
     *
     * @return string|null Returns the date/time after which the response is considered stale.
     */
    public function getExpires()
    {
        return (string) $this->getHeader('Expires');
    }

    /**
     * Get the Last-Modified HTTP header
     *
     * @return string|null Returns the last modified date for the requested object, in RFC 2822 format
     *                     (e.g. Tue, 15 Nov 1994 12:45:26 GMT)
     */
    public function getLastModified()
    {
        return (string) $this->getHeader('Last-Modified');
    }

    /**
     * Get the Location HTTP header
     *
     * @return string|null Used in redirection, or when a new resource has been created.
     */
    public function getLocation()
    {
        return (string) $this->getHeader('Location');
    }

    /**
     * Get the Pragma HTTP header
     *
     * @return Header|null Returns the implementation-specific headers that may have various effects anywhere along
     *                     the request-response chain.
     */
    public function getPragma()
    {
        return (string) $this->getHeader('Pragma');
    }

    /**
     * Get the Proxy-Authenticate HTTP header
     *
     * @return string|null Authentication to access the proxy (e.g. Basic)
     */
    public function getProxyAuthenticate()
    {
        return (string) $this->getHeader('Proxy-Authenticate');
    }

    /**
     * Get the Retry-After HTTP header
     *
     * @return int|null If an entity is temporarily unavailable, this instructs the client to try again after a
     *                  specified period of time.
     */
    public function getRetryAfter()
    {
        return (string) $this->getHeader('Retry-After');
    }

    /**
     * Get the Server HTTP header
     *
     * @return string|null A name for the server
     */
    public function getServer()
    {
        return (string)  $this->getHeader('Server');
    }

    /**
     * Get the Set-Cookie HTTP header
     *
     * @return string|null An HTTP cookie.
     */
    public function getSetCookie()
    {
        return (string) $this->getHeader('Set-Cookie');
    }

    /**
     * Get the Trailer HTTP header
     *
     * @return string|null The Trailer general field value indicates that the given set of header fields is present in
     *                     the trailer of a message encoded with chunked transfer-coding.
     */
    public function getTrailer()
    {
        return (string) $this->getHeader('Trailer');
    }

    /**
     * Get the Transfer-Encoding HTTP header
     *
     * @return string|null The form of encoding used to safely transfer the entity to the user
     */
    public function getTransferEncoding()
    {
        return (string) $this->getHeader('Transfer-Encoding');
    }

    /**
     * Get the Vary HTTP header
     *
     * @return string|null Tells downstream proxies how to match future request headers to decide whether the cached
     *                     response can be used rather than requesting a fresh one from the origin server.
     */
    public function getVary()
    {
        return (string) $this->getHeader('Vary');
    }

    /**
     * Get the Via HTTP header
     *
     * @return string|null Informs the client of proxies through which the response was sent.
     */
    public function getVia()
    {
        return (string) $this->getHeader('Via');
    }

    /**
     * Get the Warning HTTP header
     *
     * @return string|null A general warning about possible problems with the entity body
     */
    public function getWarning()
    {
        return (string) $this->getHeader('Warning');
    }

    /**
     * Get the WWW-Authenticate HTTP header
     *
     * @return string|null Indicates the authentication scheme that should be used to access the requested entity
     */
    public function getWwwAuthenticate()
    {
        return (string) $this->getHeader('WWW-Authenticate');
    }

    /**
     * Checks if HTTP Status code is a Client Error (4xx)
     *
     * @return bool
     */
    public function isClientError()
    {
        return $this->statusCode >= 400 && $this->statusCode < 500;
    }

    /**
     * Checks if HTTP Status code is Server OR Client Error (4xx or 5xx)
     *
     * @return boolean
     */
    public function isError()
    {
        return $this->isClientError() || $this->isServerError();
    }

    /**
     * Checks if HTTP Status code is Information (1xx)
     *
     * @return bool
     */
    public function isInformational()
    {
        return $this->statusCode < 200;
    }

    /**
     * Checks if HTTP Status code is a Redirect (3xx)
     *
     * @return bool
     */
    public function isRedirect()
    {
        return $this->statusCode >= 300 && $this->statusCode < 400;
    }

    /**
     * Checks if HTTP Status code is Server Error (5xx)
     *
     * @return bool
     */
    public function isServerError()
    {
        return $this->statusCode >= 500 && $this->statusCode < 600;
    }

    /**
     * Checks if HTTP Status code is Successful (2xx | 304)
     *
     * @return bool
     */
    public function isSuccessful()
    {
        return ($this->statusCode >= 200 && $this->statusCode < 300) || $this->statusCode == 304;
    }

    /**
     * Check if the response can be cached based on the response headers
     *
     * @return bool Returns TRUE if the response can be cached or false if not
     */
    public function canCache()
    {
        // Check if the response is cacheable based on the code
        if (!in_array((int) $this->getStatusCode(), self::$cacheResponseCodes)) {
            return false;
        }

        // Make sure a valid body was returned and can be cached
        if ((!$this->getBody()->isReadable() || !$this->getBody()->isSeekable())
            && ($this->getContentLength() > 0 || $this->getTransferEncoding() == 'chunked')) {
            return false;
        }

        // Never cache no-store resources (this is a private cache, so private
        // can be cached)
        if ($this->getHeader('Cache-Control') && $this->getHeader('Cache-Control')->hasDirective('no-store')) {
            return false;
        }

        return $this->isFresh() || $this->getFreshness() === null || $this->canValidate();
    }

    /**
     * Gets the number of seconds from the current time in which this response is still considered fresh
     *
     * @return int|null Returns the number of seconds
     */
    public function getMaxAge()
    {
        if ($header = $this->getHeader('Cache-Control')) {
            // s-max-age, then max-age, then Expires
            if ($age = $header->getDirective('s-maxage')) {
                return $age;
            }
            if ($age = $header->getDirective('max-age')) {
                return $age;
            }
        }

        if ($this->getHeader('Expires')) {
            return strtotime($this->getExpires()) - time();
        }

        return null;
    }

    /**
     * Check if the response is considered fresh.
     *
     * A response is considered fresh when its age is less than or equal to the freshness lifetime (maximum age) of the
     * response.
     *
     * @return bool|null
     */
    public function isFresh()
    {
        $fresh = $this->getFreshness();

        return $fresh === null ? null : $fresh >= 0;
    }

    /**
     * Check if the response can be validated against the origin server using a conditional GET request.
     *
     * @return bool
     */
    public function canValidate()
    {
        return $this->getEtag() || $this->getLastModified();
    }

    /**
     * Get the freshness of the response by returning the difference of the maximum lifetime of the response and the
     * age of the response (max-age - age).
     *
     * Freshness values less than 0 mean that the response is no longer fresh and is ABS(freshness) seconds expired.
     * Freshness values of greater than zero is the number of seconds until the response is no longer fresh. A NULL
     * result means that no freshness information is available.
     *
     * @return int
     */
    public function getFreshness()
    {
        $maxAge = $this->getMaxAge();
        $age = $this->calculateAge();

        return $maxAge && $age ? ($maxAge - $age) : null;
    }

    /**
     * Parse the JSON response body and return an array
     *
     * @return array|string|int|bool|float
     * @throws RuntimeException if the response body is not in JSON format
     */
    public function json()
    {
        $data = json_decode((string) $this->body, true);
        if (JSON_ERROR_NONE !== json_last_error()) {
            throw new RuntimeException('Unable to parse response body into JSON: ' . json_last_error());
        }

        return $data === null ? array() : $data;
    }

    /**
     * Parse the XML response body and return a \SimpleXMLElement.
     *
     * In order to prevent XXE attacks, this method disables loading external
     * entities. If you rely on external entities, then you must parse the
     * XML response manually by accessing the response body directly.
     *
     * @return \SimpleXMLElement
     * @throws RuntimeException if the response body is not in XML format
     * @link http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html
     */
    public function xml()
    {
        $errorMessage = null;
        $internalErrors = libxml_use_internal_errors(true);
        $disableEntities = libxml_disable_entity_loader(true);
        libxml_clear_errors();

        try {
            $xml = new \SimpleXMLElement((string) $this->body ?: '<root />', LIBXML_NONET);
            if ($error = libxml_get_last_error()) {
                $errorMessage = $error->message;
            }
        } catch (\Exception $e) {
            $errorMessage = $e->getMessage();
        }

        libxml_clear_errors();
        libxml_use_internal_errors($internalErrors);
        libxml_disable_entity_loader($disableEntities);

        if ($errorMessage) {
            throw new RuntimeException('Unable to parse response body into XML: ' . $errorMessage);
        }

        return $xml;
    }

    /**
     * Get the redirect count of this response
     *
     * @return int
     */
    public function getRedirectCount()
    {
        return (int) $this->params->get(RedirectPlugin::REDIRECT_COUNT);
    }

    /**
     * Set the effective URL that resulted in this response (e.g. the last redirect URL)
     *
     * @param string $url The effective URL
     *
     * @return self
     */
    public function setEffectiveUrl($url)
    {
        $this->effectiveUrl = $url;

        return $this;
    }

    /**
     * Get the effective URL that resulted in this response (e.g. the last redirect URL)
     *
     * @return string
     */
    public function getEffectiveUrl()
    {
        return $this->effectiveUrl;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function getPreviousResponse()
    {
        Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin.');
        return null;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function setRequest($request)
    {
        Version::warn(__METHOD__ . ' is deprecated');
        return $this;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function getRequest()
    {
        Version::warn(__METHOD__ . ' is deprecated');
        return null;
    }
}
<?php

namespace Guzzle\Http;

/**
 * Provides mappings of file extensions to mimetypes
 * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types
 */
class Mimetypes
{
    /** @var self */
    protected static $instance;

    /** @var array Mapping of extension to mimetype */
    protected $mimetypes = array(
        '3dml' => 'text/vnd.in3d.3dml',
        '3g2' => 'video/3gpp2',
        '3gp' => 'video/3gpp',
        '7z' => 'application/x-7z-compressed',
        'aab' => 'application/x-authorware-bin',
        'aac' => 'audio/x-aac',
        'aam' => 'application/x-authorware-map',
        'aas' => 'application/x-authorware-seg',
        'abw' => 'application/x-abiword',
        'ac' => 'application/pkix-attr-cert',
        'acc' => 'application/vnd.americandynamics.acc',
        'ace' => 'application/x-ace-compressed',
        'acu' => 'application/vnd.acucobol',
        'acutc' => 'application/vnd.acucorp',
        'adp' => 'audio/adpcm',
        'aep' => 'application/vnd.audiograph',
        'afm' => 'application/x-font-type1',
        'afp' => 'application/vnd.ibm.modcap',
        'ahead' => 'application/vnd.ahead.space',
        'ai' => 'application/postscript',
        'aif' => 'audio/x-aiff',
        'aifc' => 'audio/x-aiff',
        'aiff' => 'audio/x-aiff',
        'air' => 'application/vnd.adobe.air-application-installer-package+zip',
        'ait' => 'application/vnd.dvb.ait',
        'ami' => 'application/vnd.amiga.ami',
        'apk' => 'application/vnd.android.package-archive',
        'application' => 'application/x-ms-application',
        'apr' => 'application/vnd.lotus-approach',
        'asa' => 'text/plain',
        'asax' => 'application/octet-stream',
        'asc' => 'application/pgp-signature',
        'ascx' => 'text/plain',
        'asf' => 'video/x-ms-asf',
        'ashx' => 'text/plain',
        'asm' => 'text/x-asm',
        'asmx' => 'text/plain',
        'aso' => 'application/vnd.accpac.simply.aso',
        'asp' => 'text/plain',
        'aspx' => 'text/plain',
        'asx' => 'video/x-ms-asf',
        'atc' => 'application/vnd.acucorp',
        'atom' => 'application/atom+xml',
        'atomcat' => 'application/atomcat+xml',
        'atomsvc' => 'application/atomsvc+xml',
        'atx' => 'application/vnd.antix.game-component',
        'au' => 'audio/basic',
        'avi' => 'video/x-msvideo',
        'aw' => 'application/applixware',
        'axd' => 'text/plain',
        'azf' => 'application/vnd.airzip.filesecure.azf',
        'azs' => 'application/vnd.airzip.filesecure.azs',
        'azw' => 'application/vnd.amazon.ebook',
        'bat' => 'application/x-msdownload',
        'bcpio' => 'application/x-bcpio',
        'bdf' => 'application/x-font-bdf',
        'bdm' => 'application/vnd.syncml.dm+wbxml',
        'bed' => 'application/vnd.realvnc.bed',
        'bh2' => 'application/vnd.fujitsu.oasysprs',
        'bin' => 'application/octet-stream',
        'bmi' => 'application/vnd.bmi',
        'bmp' => 'image/bmp',
        'book' => 'application/vnd.framemaker',
        'box' => 'application/vnd.previewsystems.box',
        'boz' => 'application/x-bzip2',
        'bpk' => 'application/octet-stream',
        'btif' => 'image/prs.btif',
        'bz' => 'application/x-bzip',
        'bz2' => 'application/x-bzip2',
        'c' => 'text/x-c',
        'c11amc' => 'application/vnd.cluetrust.cartomobile-config',
        'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg',
        'c4d' => 'application/vnd.clonk.c4group',
        'c4f' => 'application/vnd.clonk.c4group',
        'c4g' => 'application/vnd.clonk.c4group',
        'c4p' => 'application/vnd.clonk.c4group',
        'c4u' => 'application/vnd.clonk.c4group',
        'cab' => 'application/vnd.ms-cab-compressed',
        'car' => 'application/vnd.curl.car',
        'cat' => 'application/vnd.ms-pki.seccat',
        'cc' => 'text/x-c',
        'cct' => 'application/x-director',
        'ccxml' => 'application/ccxml+xml',
        'cdbcmsg' => 'application/vnd.contact.cmsg',
        'cdf' => 'application/x-netcdf',
        'cdkey' => 'application/vnd.mediastation.cdkey',
        'cdmia' => 'application/cdmi-capability',
        'cdmic' => 'application/cdmi-container',
        'cdmid' => 'application/cdmi-domain',
        'cdmio' => 'application/cdmi-object',
        'cdmiq' => 'application/cdmi-queue',
        'cdx' => 'chemical/x-cdx',
        'cdxml' => 'application/vnd.chemdraw+xml',
        'cdy' => 'application/vnd.cinderella',
        'cer' => 'application/pkix-cert',
        'cfc' => 'application/x-coldfusion',
        'cfm' => 'application/x-coldfusion',
        'cgm' => 'image/cgm',
        'chat' => 'application/x-chat',
        'chm' => 'application/vnd.ms-htmlhelp',
        'chrt' => 'application/vnd.kde.kchart',
        'cif' => 'chemical/x-cif',
        'cii' => 'application/vnd.anser-web-certificate-issue-initiation',
        'cil' => 'application/vnd.ms-artgalry',
        'cla' => 'application/vnd.claymore',
        'class' => 'application/java-vm',
        'clkk' => 'application/vnd.crick.clicker.keyboard',
        'clkp' => 'application/vnd.crick.clicker.palette',
        'clkt' => 'application/vnd.crick.clicker.template',
        'clkw' => 'application/vnd.crick.clicker.wordbank',
        'clkx' => 'application/vnd.crick.clicker',
        'clp' => 'application/x-msclip',
        'cmc' => 'application/vnd.cosmocaller',
        'cmdf' => 'chemical/x-cmdf',
        'cml' => 'chemical/x-cml',
        'cmp' => 'application/vnd.yellowriver-custom-menu',
        'cmx' => 'image/x-cmx',
        'cod' => 'application/vnd.rim.cod',
        'com' => 'application/x-msdownload',
        'conf' => 'text/plain',
        'cpio' => 'application/x-cpio',
        'cpp' => 'text/x-c',
        'cpt' => 'application/mac-compactpro',
        'crd' => 'application/x-mscardfile',
        'crl' => 'application/pkix-crl',
        'crt' => 'application/x-x509-ca-cert',
        'cryptonote' => 'application/vnd.rig.cryptonote',
        'cs' => 'text/plain',
        'csh' => 'application/x-csh',
        'csml' => 'chemical/x-csml',
        'csp' => 'application/vnd.commonspace',
        'css' => 'text/css',
        'cst' => 'application/x-director',
        'csv' => 'text/csv',
        'cu' => 'application/cu-seeme',
        'curl' => 'text/vnd.curl',
        'cww' => 'application/prs.cww',
        'cxt' => 'application/x-director',
        'cxx' => 'text/x-c',
        'dae' => 'model/vnd.collada+xml',
        'daf' => 'application/vnd.mobius.daf',
        'dataless' => 'application/vnd.fdsn.seed',
        'davmount' => 'application/davmount+xml',
        'dcr' => 'application/x-director',
        'dcurl' => 'text/vnd.curl.dcurl',
        'dd2' => 'application/vnd.oma.dd2+xml',
        'ddd' => 'application/vnd.fujixerox.ddd',
        'deb' => 'application/x-debian-package',
        'def' => 'text/plain',
        'deploy' => 'application/octet-stream',
        'der' => 'application/x-x509-ca-cert',
        'dfac' => 'application/vnd.dreamfactory',
        'dic' => 'text/x-c',
        'dir' => 'application/x-director',
        'dis' => 'application/vnd.mobius.dis',
        'dist' => 'application/octet-stream',
        'distz' => 'application/octet-stream',
        'djv' => 'image/vnd.djvu',
        'djvu' => 'image/vnd.djvu',
        'dll' => 'application/x-msdownload',
        'dmg' => 'application/octet-stream',
        'dms' => 'application/octet-stream',
        'dna' => 'application/vnd.dna',
        'doc' => 'application/msword',
        'docm' => 'application/vnd.ms-word.document.macroenabled.12',
        'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        'dot' => 'application/msword',
        'dotm' => 'application/vnd.ms-word.template.macroenabled.12',
        'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
        'dp' => 'application/vnd.osgi.dp',
        'dpg' => 'application/vnd.dpgraph',
        'dra' => 'audio/vnd.dra',
        'dsc' => 'text/prs.lines.tag',
        'dssc' => 'application/dssc+der',
        'dtb' => 'application/x-dtbook+xml',
        'dtd' => 'application/xml-dtd',
        'dts' => 'audio/vnd.dts',
        'dtshd' => 'audio/vnd.dts.hd',
        'dump' => 'application/octet-stream',
        'dvi' => 'application/x-dvi',
        'dwf' => 'model/vnd.dwf',
        'dwg' => 'image/vnd.dwg',
        'dxf' => 'image/vnd.dxf',
        'dxp' => 'application/vnd.spotfire.dxp',
        'dxr' => 'application/x-director',
        'ecelp4800' => 'audio/vnd.nuera.ecelp4800',
        'ecelp7470' => 'audio/vnd.nuera.ecelp7470',
        'ecelp9600' => 'audio/vnd.nuera.ecelp9600',
        'ecma' => 'application/ecmascript',
        'edm' => 'application/vnd.novadigm.edm',
        'edx' => 'application/vnd.novadigm.edx',
        'efif' => 'application/vnd.picsel',
        'ei6' => 'application/vnd.pg.osasli',
        'elc' => 'application/octet-stream',
        'eml' => 'message/rfc822',
        'emma' => 'application/emma+xml',
        'eol' => 'audio/vnd.digital-winds',
        'eot' => 'application/vnd.ms-fontobject',
        'eps' => 'application/postscript',
        'epub' => 'application/epub+zip',
        'es3' => 'application/vnd.eszigno3+xml',
        'esf' => 'application/vnd.epson.esf',
        'et3' => 'application/vnd.eszigno3+xml',
        'etx' => 'text/x-setext',
        'exe' => 'application/x-msdownload',
        'exi' => 'application/exi',
        'ext' => 'application/vnd.novadigm.ext',
        'ez' => 'application/andrew-inset',
        'ez2' => 'application/vnd.ezpix-album',
        'ez3' => 'application/vnd.ezpix-package',
        'f' => 'text/x-fortran',
        'f4v' => 'video/x-f4v',
        'f77' => 'text/x-fortran',
        'f90' => 'text/x-fortran',
        'fbs' => 'image/vnd.fastbidsheet',
        'fcs' => 'application/vnd.isac.fcs',
        'fdf' => 'application/vnd.fdf',
        'fe_launch' => 'application/vnd.denovo.fcselayout-link',
        'fg5' => 'application/vnd.fujitsu.oasysgp',
        'fgd' => 'application/x-director',
        'fh' => 'image/x-freehand',
        'fh4' => 'image/x-freehand',
        'fh5' => 'image/x-freehand',
        'fh7' => 'image/x-freehand',
        'fhc' => 'image/x-freehand',
        'fig' => 'application/x-xfig',
        'fli' => 'video/x-fli',
        'flo' => 'application/vnd.micrografx.flo',
        'flv' => 'video/x-flv',
        'flw' => 'application/vnd.kde.kivio',
        'flx' => 'text/vnd.fmi.flexstor',
        'fly' => 'text/vnd.fly',
        'fm' => 'application/vnd.framemaker',
        'fnc' => 'application/vnd.frogans.fnc',
        'for' => 'text/x-fortran',
        'fpx' => 'image/vnd.fpx',
        'frame' => 'application/vnd.framemaker',
        'fsc' => 'application/vnd.fsc.weblaunch',
        'fst' => 'image/vnd.fst',
        'ftc' => 'application/vnd.fluxtime.clip',
        'fti' => 'application/vnd.anser-web-funds-transfer-initiation',
        'fvt' => 'video/vnd.fvt',
        'fxp' => 'application/vnd.adobe.fxp',
        'fxpl' => 'application/vnd.adobe.fxp',
        'fzs' => 'application/vnd.fuzzysheet',
        'g2w' => 'application/vnd.geoplan',
        'g3' => 'image/g3fax',
        'g3w' => 'application/vnd.geospace',
        'gac' => 'application/vnd.groove-account',
        'gdl' => 'model/vnd.gdl',
        'geo' => 'application/vnd.dynageo',
        'gex' => 'application/vnd.geometry-explorer',
        'ggb' => 'application/vnd.geogebra.file',
        'ggt' => 'application/vnd.geogebra.tool',
        'ghf' => 'application/vnd.groove-help',
        'gif' => 'image/gif',
        'gim' => 'application/vnd.groove-identity-message',
        'gmx' => 'application/vnd.gmx',
        'gnumeric' => 'application/x-gnumeric',
        'gph' => 'application/vnd.flographit',
        'gqf' => 'application/vnd.grafeq',
        'gqs' => 'application/vnd.grafeq',
        'gram' => 'application/srgs',
        'gre' => 'application/vnd.geometry-explorer',
        'grv' => 'application/vnd.groove-injector',
        'grxml' => 'application/srgs+xml',
        'gsf' => 'application/x-font-ghostscript',
        'gtar' => 'application/x-gtar',
        'gtm' => 'application/vnd.groove-tool-message',
        'gtw' => 'model/vnd.gtw',
        'gv' => 'text/vnd.graphviz',
        'gxt' => 'application/vnd.geonext',
        'h' => 'text/x-c',
        'h261' => 'video/h261',
        'h263' => 'video/h263',
        'h264' => 'video/h264',
        'hal' => 'application/vnd.hal+xml',
        'hbci' => 'application/vnd.hbci',
        'hdf' => 'application/x-hdf',
        'hh' => 'text/x-c',
        'hlp' => 'application/winhlp',
        'hpgl' => 'application/vnd.hp-hpgl',
        'hpid' => 'application/vnd.hp-hpid',
        'hps' => 'application/vnd.hp-hps',
        'hqx' => 'application/mac-binhex40',
        'hta' => 'application/octet-stream',
        'htc' => 'text/html',
        'htke' => 'application/vnd.kenameaapp',
        'htm' => 'text/html',
        'html' => 'text/html',
        'hvd' => 'application/vnd.yamaha.hv-dic',
        'hvp' => 'application/vnd.yamaha.hv-voice',
        'hvs' => 'application/vnd.yamaha.hv-script',
        'i2g' => 'application/vnd.intergeo',
        'icc' => 'application/vnd.iccprofile',
        'ice' => 'x-conference/x-cooltalk',
        'icm' => 'application/vnd.iccprofile',
        'ico' => 'image/x-icon',
        'ics' => 'text/calendar',
        'ief' => 'image/ief',
        'ifb' => 'text/calendar',
        'ifm' => 'application/vnd.shana.informed.formdata',
        'iges' => 'model/iges',
        'igl' => 'application/vnd.igloader',
        'igm' => 'application/vnd.insors.igm',
        'igs' => 'model/iges',
        'igx' => 'application/vnd.micrografx.igx',
        'iif' => 'application/vnd.shana.informed.interchange',
        'imp' => 'application/vnd.accpac.simply.imp',
        'ims' => 'application/vnd.ms-ims',
        'in' => 'text/plain',
        'ini' => 'text/plain',
        'ipfix' => 'application/ipfix',
        'ipk' => 'application/vnd.shana.informed.package',
        'irm' => 'application/vnd.ibm.rights-management',
        'irp' => 'application/vnd.irepository.package+xml',
        'iso' => 'application/octet-stream',
        'itp' => 'application/vnd.shana.informed.formtemplate',
        'ivp' => 'application/vnd.immervision-ivp',
        'ivu' => 'application/vnd.immervision-ivu',
        'jad' => 'text/vnd.sun.j2me.app-descriptor',
        'jam' => 'application/vnd.jam',
        'jar' => 'application/java-archive',
        'java' => 'text/x-java-source',
        'jisp' => 'application/vnd.jisp',
        'jlt' => 'application/vnd.hp-jlyt',
        'jnlp' => 'application/x-java-jnlp-file',
        'joda' => 'application/vnd.joost.joda-archive',
        'jpe' => 'image/jpeg',
        'jpeg' => 'image/jpeg',
        'jpg' => 'image/jpeg',
        'jpgm' => 'video/jpm',
        'jpgv' => 'video/jpeg',
        'jpm' => 'video/jpm',
        'js' => 'text/javascript',
        'json' => 'application/json',
        'kar' => 'audio/midi',
        'karbon' => 'application/vnd.kde.karbon',
        'kfo' => 'application/vnd.kde.kformula',
        'kia' => 'application/vnd.kidspiration',
        'kml' => 'application/vnd.google-earth.kml+xml',
        'kmz' => 'application/vnd.google-earth.kmz',
        'kne' => 'application/vnd.kinar',
        'knp' => 'application/vnd.kinar',
        'kon' => 'application/vnd.kde.kontour',
        'kpr' => 'application/vnd.kde.kpresenter',
        'kpt' => 'application/vnd.kde.kpresenter',
        'ksp' => 'application/vnd.kde.kspread',
        'ktr' => 'application/vnd.kahootz',
        'ktx' => 'image/ktx',
        'ktz' => 'application/vnd.kahootz',
        'kwd' => 'application/vnd.kde.kword',
        'kwt' => 'application/vnd.kde.kword',
        'lasxml' => 'application/vnd.las.las+xml',
        'latex' => 'application/x-latex',
        'lbd' => 'application/vnd.llamagraphics.life-balance.desktop',
        'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml',
        'les' => 'application/vnd.hhe.lesson-player',
        'lha' => 'application/octet-stream',
        'link66' => 'application/vnd.route66.link66+xml',
        'list' => 'text/plain',
        'list3820' => 'application/vnd.ibm.modcap',
        'listafp' => 'application/vnd.ibm.modcap',
        'log' => 'text/plain',
        'lostxml' => 'application/lost+xml',
        'lrf' => 'application/octet-stream',
        'lrm' => 'application/vnd.ms-lrm',
        'ltf' => 'application/vnd.frogans.ltf',
        'lvp' => 'audio/vnd.lucent.voice',
        'lwp' => 'application/vnd.lotus-wordpro',
        'lzh' => 'application/octet-stream',
        'm13' => 'application/x-msmediaview',
        'm14' => 'application/x-msmediaview',
        'm1v' => 'video/mpeg',
        'm21' => 'application/mp21',
        'm2a' => 'audio/mpeg',
        'm2v' => 'video/mpeg',
        'm3a' => 'audio/mpeg',
        'm3u' => 'audio/x-mpegurl',
        'm3u8' => 'application/vnd.apple.mpegurl',
        'm4a' => 'audio/mp4',
        'm4u' => 'video/vnd.mpegurl',
        'm4v' => 'video/mp4',
        'ma' => 'application/mathematica',
        'mads' => 'application/mads+xml',
        'mag' => 'application/vnd.ecowin.chart',
        'maker' => 'application/vnd.framemaker',
        'man' => 'text/troff',
        'mathml' => 'application/mathml+xml',
        'mb' => 'application/mathematica',
        'mbk' => 'application/vnd.mobius.mbk',
        'mbox' => 'application/mbox',
        'mc1' => 'application/vnd.medcalcdata',
        'mcd' => 'application/vnd.mcd',
        'mcurl' => 'text/vnd.curl.mcurl',
        'mdb' => 'application/x-msaccess',
        'mdi' => 'image/vnd.ms-modi',
        'me' => 'text/troff',
        'mesh' => 'model/mesh',
        'meta4' => 'application/metalink4+xml',
        'mets' => 'application/mets+xml',
        'mfm' => 'application/vnd.mfmp',
        'mgp' => 'application/vnd.osgeo.mapguide.package',
        'mgz' => 'application/vnd.proteus.magazine',
        'mid' => 'audio/midi',
        'midi' => 'audio/midi',
        'mif' => 'application/vnd.mif',
        'mime' => 'message/rfc822',
        'mj2' => 'video/mj2',
        'mjp2' => 'video/mj2',
        'mlp' => 'application/vnd.dolby.mlp',
        'mmd' => 'application/vnd.chipnuts.karaoke-mmd',
        'mmf' => 'application/vnd.smaf',
        'mmr' => 'image/vnd.fujixerox.edmics-mmr',
        'mny' => 'application/x-msmoney',
        'mobi' => 'application/x-mobipocket-ebook',
        'mods' => 'application/mods+xml',
        'mov' => 'video/quicktime',
        'movie' => 'video/x-sgi-movie',
        'mp2' => 'audio/mpeg',
        'mp21' => 'application/mp21',
        'mp2a' => 'audio/mpeg',
        'mp3' => 'audio/mpeg',
        'mp4' => 'video/mp4',
        'mp4a' => 'audio/mp4',
        'mp4s' => 'application/mp4',
        'mp4v' => 'video/mp4',
        'mpc' => 'application/vnd.mophun.certificate',
        'mpe' => 'video/mpeg',
        'mpeg' => 'video/mpeg',
        'mpg' => 'video/mpeg',
        'mpg4' => 'video/mp4',
        'mpga' => 'audio/mpeg',
        'mpkg' => 'application/vnd.apple.installer+xml',
        'mpm' => 'application/vnd.blueice.multipass',
        'mpn' => 'application/vnd.mophun.application',
        'mpp' => 'application/vnd.ms-project',
        'mpt' => 'application/vnd.ms-project',
        'mpy' => 'application/vnd.ibm.minipay',
        'mqy' => 'application/vnd.mobius.mqy',
        'mrc' => 'application/marc',
        'mrcx' => 'application/marcxml+xml',
        'ms' => 'text/troff',
        'mscml' => 'application/mediaservercontrol+xml',
        'mseed' => 'application/vnd.fdsn.mseed',
        'mseq' => 'application/vnd.mseq',
        'msf' => 'application/vnd.epson.msf',
        'msh' => 'model/mesh',
        'msi' => 'application/x-msdownload',
        'msl' => 'application/vnd.mobius.msl',
        'msty' => 'application/vnd.muvee.style',
        'mts' => 'model/vnd.mts',
        'mus' => 'application/vnd.musician',
        'musicxml' => 'application/vnd.recordare.musicxml+xml',
        'mvb' => 'application/x-msmediaview',
        'mwf' => 'application/vnd.mfer',
        'mxf' => 'application/mxf',
        'mxl' => 'application/vnd.recordare.musicxml',
        'mxml' => 'application/xv+xml',
        'mxs' => 'application/vnd.triscape.mxs',
        'mxu' => 'video/vnd.mpegurl',
        'n-gage' => 'application/vnd.nokia.n-gage.symbian.install',
        'n3' => 'text/n3',
        'nb' => 'application/mathematica',
        'nbp' => 'application/vnd.wolfram.player',
        'nc' => 'application/x-netcdf',
        'ncx' => 'application/x-dtbncx+xml',
        'ngdat' => 'application/vnd.nokia.n-gage.data',
        'nlu' => 'application/vnd.neurolanguage.nlu',
        'nml' => 'application/vnd.enliven',
        'nnd' => 'application/vnd.noblenet-directory',
        'nns' => 'application/vnd.noblenet-sealer',
        'nnw' => 'application/vnd.noblenet-web',
        'npx' => 'image/vnd.net-fpx',
        'nsf' => 'application/vnd.lotus-notes',
        'oa2' => 'application/vnd.fujitsu.oasys2',
        'oa3' => 'application/vnd.fujitsu.oasys3',
        'oas' => 'application/vnd.fujitsu.oasys',
        'obd' => 'application/x-msbinder',
        'oda' => 'application/oda',
        'odb' => 'application/vnd.oasis.opendocument.database',
        'odc' => 'application/vnd.oasis.opendocument.chart',
        'odf' => 'application/vnd.oasis.opendocument.formula',
        'odft' => 'application/vnd.oasis.opendocument.formula-template',
        'odg' => 'application/vnd.oasis.opendocument.graphics',
        'odi' => 'application/vnd.oasis.opendocument.image',
        'odm' => 'application/vnd.oasis.opendocument.text-master',
        'odp' => 'application/vnd.oasis.opendocument.presentation',
        'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
        'odt' => 'application/vnd.oasis.opendocument.text',
        'oga' => 'audio/ogg',
        'ogg' => 'audio/ogg',
        'ogv' => 'video/ogg',
        'ogx' => 'application/ogg',
        'onepkg' => 'application/onenote',
        'onetmp' => 'application/onenote',
        'onetoc' => 'application/onenote',
        'onetoc2' => 'application/onenote',
        'opf' => 'application/oebps-package+xml',
        'oprc' => 'application/vnd.palm',
        'org' => 'application/vnd.lotus-organizer',
        'osf' => 'application/vnd.yamaha.openscoreformat',
        'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml',
        'otc' => 'application/vnd.oasis.opendocument.chart-template',
        'otf' => 'application/x-font-otf',
        'otg' => 'application/vnd.oasis.opendocument.graphics-template',
        'oth' => 'application/vnd.oasis.opendocument.text-web',
        'oti' => 'application/vnd.oasis.opendocument.image-template',
        'otp' => 'application/vnd.oasis.opendocument.presentation-template',
        'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
        'ott' => 'application/vnd.oasis.opendocument.text-template',
        'oxt' => 'application/vnd.openofficeorg.extension',
        'p' => 'text/x-pascal',
        'p10' => 'application/pkcs10',
        'p12' => 'application/x-pkcs12',
        'p7b' => 'application/x-pkcs7-certificates',
        'p7c' => 'application/pkcs7-mime',
        'p7m' => 'application/pkcs7-mime',
        'p7r' => 'application/x-pkcs7-certreqresp',
        'p7s' => 'application/pkcs7-signature',
        'p8' => 'application/pkcs8',
        'pas' => 'text/x-pascal',
        'paw' => 'application/vnd.pawaafile',
        'pbd' => 'application/vnd.powerbuilder6',
        'pbm' => 'image/x-portable-bitmap',
        'pcf' => 'application/x-font-pcf',
        'pcl' => 'application/vnd.hp-pcl',
        'pclxl' => 'application/vnd.hp-pclxl',
        'pct' => 'image/x-pict',
        'pcurl' => 'application/vnd.curl.pcurl',
        'pcx' => 'image/x-pcx',
        'pdb' => 'application/vnd.palm',
        'pdf' => 'application/pdf',
        'pfa' => 'application/x-font-type1',
        'pfb' => 'application/x-font-type1',
        'pfm' => 'application/x-font-type1',
        'pfr' => 'application/font-tdpfr',
        'pfx' => 'application/x-pkcs12',
        'pgm' => 'image/x-portable-graymap',
        'pgn' => 'application/x-chess-pgn',
        'pgp' => 'application/pgp-encrypted',
        'php' => 'text/x-php',
        'phps' => 'application/x-httpd-phps',
        'pic' => 'image/x-pict',
        'pkg' => 'application/octet-stream',
        'pki' => 'application/pkixcmp',
        'pkipath' => 'application/pkix-pkipath',
        'plb' => 'application/vnd.3gpp.pic-bw-large',
        'plc' => 'application/vnd.mobius.plc',
        'plf' => 'application/vnd.pocketlearn',
        'pls' => 'application/pls+xml',
        'pml' => 'application/vnd.ctc-posml',
        'png' => 'image/png',
        'pnm' => 'image/x-portable-anymap',
        'portpkg' => 'application/vnd.macports.portpkg',
        'pot' => 'application/vnd.ms-powerpoint',
        'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12',
        'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
        'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12',
        'ppd' => 'application/vnd.cups-ppd',
        'ppm' => 'image/x-portable-pixmap',
        'pps' => 'application/vnd.ms-powerpoint',
        'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12',
        'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
        'ppt' => 'application/vnd.ms-powerpoint',
        'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12',
        'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
        'pqa' => 'application/vnd.palm',
        'prc' => 'application/x-mobipocket-ebook',
        'pre' => 'application/vnd.lotus-freelance',
        'prf' => 'application/pics-rules',
        'ps' => 'application/postscript',
        'psb' => 'application/vnd.3gpp.pic-bw-small',
        'psd' => 'image/vnd.adobe.photoshop',
        'psf' => 'application/x-font-linux-psf',
        'pskcxml' => 'application/pskc+xml',
        'ptid' => 'application/vnd.pvi.ptid1',
        'pub' => 'application/x-mspublisher',
        'pvb' => 'application/vnd.3gpp.pic-bw-var',
        'pwn' => 'application/vnd.3m.post-it-notes',
        'pya' => 'audio/vnd.ms-playready.media.pya',
        'pyv' => 'video/vnd.ms-playready.media.pyv',
        'qam' => 'application/vnd.epson.quickanime',
        'qbo' => 'application/vnd.intu.qbo',
        'qfx' => 'application/vnd.intu.qfx',
        'qps' => 'application/vnd.publishare-delta-tree',
        'qt' => 'video/quicktime',
        'qwd' => 'application/vnd.quark.quarkxpress',
        'qwt' => 'application/vnd.quark.quarkxpress',
        'qxb' => 'application/vnd.quark.quarkxpress',
        'qxd' => 'application/vnd.quark.quarkxpress',
        'qxl' => 'application/vnd.quark.quarkxpress',
        'qxt' => 'application/vnd.quark.quarkxpress',
        'ra' => 'audio/x-pn-realaudio',
        'ram' => 'audio/x-pn-realaudio',
        'rar' => 'application/x-rar-compressed',
        'ras' => 'image/x-cmu-raster',
        'rb' => 'text/plain',
        'rcprofile' => 'application/vnd.ipunplugged.rcprofile',
        'rdf' => 'application/rdf+xml',
        'rdz' => 'application/vnd.data-vision.rdz',
        'rep' => 'application/vnd.businessobjects',
        'res' => 'application/x-dtbresource+xml',
        'resx' => 'text/xml',
        'rgb' => 'image/x-rgb',
        'rif' => 'application/reginfo+xml',
        'rip' => 'audio/vnd.rip',
        'rl' => 'application/resource-lists+xml',
        'rlc' => 'image/vnd.fujixerox.edmics-rlc',
        'rld' => 'application/resource-lists-diff+xml',
        'rm' => 'application/vnd.rn-realmedia',
        'rmi' => 'audio/midi',
        'rmp' => 'audio/x-pn-realaudio-plugin',
        'rms' => 'application/vnd.jcp.javame.midlet-rms',
        'rnc' => 'application/relax-ng-compact-syntax',
        'roff' => 'text/troff',
        'rp9' => 'application/vnd.cloanto.rp9',
        'rpss' => 'application/vnd.nokia.radio-presets',
        'rpst' => 'application/vnd.nokia.radio-preset',
        'rq' => 'application/sparql-query',
        'rs' => 'application/rls-services+xml',
        'rsd' => 'application/rsd+xml',
        'rss' => 'application/rss+xml',
        'rtf' => 'application/rtf',
        'rtx' => 'text/richtext',
        's' => 'text/x-asm',
        'saf' => 'application/vnd.yamaha.smaf-audio',
        'sbml' => 'application/sbml+xml',
        'sc' => 'application/vnd.ibm.secure-container',
        'scd' => 'application/x-msschedule',
        'scm' => 'application/vnd.lotus-screencam',
        'scq' => 'application/scvp-cv-request',
        'scs' => 'application/scvp-cv-response',
        'scurl' => 'text/vnd.curl.scurl',
        'sda' => 'application/vnd.stardivision.draw',
        'sdc' => 'application/vnd.stardivision.calc',
        'sdd' => 'application/vnd.stardivision.impress',
        'sdkd' => 'application/vnd.solent.sdkm+xml',
        'sdkm' => 'application/vnd.solent.sdkm+xml',
        'sdp' => 'application/sdp',
        'sdw' => 'application/vnd.stardivision.writer',
        'see' => 'application/vnd.seemail',
        'seed' => 'application/vnd.fdsn.seed',
        'sema' => 'application/vnd.sema',
        'semd' => 'application/vnd.semd',
        'semf' => 'application/vnd.semf',
        'ser' => 'application/java-serialized-object',
        'setpay' => 'application/set-payment-initiation',
        'setreg' => 'application/set-registration-initiation',
        'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data',
        'sfs' => 'application/vnd.spotfire.sfs',
        'sgl' => 'application/vnd.stardivision.writer-global',
        'sgm' => 'text/sgml',
        'sgml' => 'text/sgml',
        'sh' => 'application/x-sh',
        'shar' => 'application/x-shar',
        'shf' => 'application/shf+xml',
        'sig' => 'application/pgp-signature',
        'silo' => 'model/mesh',
        'sis' => 'application/vnd.symbian.install',
        'sisx' => 'application/vnd.symbian.install',
        'sit' => 'application/x-stuffit',
        'sitx' => 'application/x-stuffitx',
        'skd' => 'application/vnd.koan',
        'skm' => 'application/vnd.koan',
        'skp' => 'application/vnd.koan',
        'skt' => 'application/vnd.koan',
        'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12',
        'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
        'slt' => 'application/vnd.epson.salt',
        'sm' => 'application/vnd.stepmania.stepchart',
        'smf' => 'application/vnd.stardivision.math',
        'smi' => 'application/smil+xml',
        'smil' => 'application/smil+xml',
        'snd' => 'audio/basic',
        'snf' => 'application/x-font-snf',
        'so' => 'application/octet-stream',
        'spc' => 'application/x-pkcs7-certificates',
        'spf' => 'application/vnd.yamaha.smaf-phrase',
        'spl' => 'application/x-futuresplash',
        'spot' => 'text/vnd.in3d.spot',
        'spp' => 'application/scvp-vp-response',
        'spq' => 'application/scvp-vp-request',
        'spx' => 'audio/ogg',
        'src' => 'application/x-wais-source',
        'sru' => 'application/sru+xml',
        'srx' => 'application/sparql-results+xml',
        'sse' => 'application/vnd.kodak-descriptor',
        'ssf' => 'application/vnd.epson.ssf',
        'ssml' => 'application/ssml+xml',
        'st' => 'application/vnd.sailingtracker.track',
        'stc' => 'application/vnd.sun.xml.calc.template',
        'std' => 'application/vnd.sun.xml.draw.template',
        'stf' => 'application/vnd.wt.stf',
        'sti' => 'application/vnd.sun.xml.impress.template',
        'stk' => 'application/hyperstudio',
        'stl' => 'application/vnd.ms-pki.stl',
        'str' => 'application/vnd.pg.format',
        'stw' => 'application/vnd.sun.xml.writer.template',
        'sub' => 'image/vnd.dvb.subtitle',
        'sus' => 'application/vnd.sus-calendar',
        'susp' => 'application/vnd.sus-calendar',
        'sv4cpio' => 'application/x-sv4cpio',
        'sv4crc' => 'application/x-sv4crc',
        'svc' => 'application/vnd.dvb.service',
        'svd' => 'application/vnd.svd',
        'svg' => 'image/svg+xml',
        'svgz' => 'image/svg+xml',
        'swa' => 'application/x-director',
        'swf' => 'application/x-shockwave-flash',
        'swi' => 'application/vnd.aristanetworks.swi',
        'sxc' => 'application/vnd.sun.xml.calc',
        'sxd' => 'application/vnd.sun.xml.draw',
        'sxg' => 'application/vnd.sun.xml.writer.global',
        'sxi' => 'application/vnd.sun.xml.impress',
        'sxm' => 'application/vnd.sun.xml.math',
        'sxw' => 'application/vnd.sun.xml.writer',
        't' => 'text/troff',
        'tao' => 'application/vnd.tao.intent-module-archive',
        'tar' => 'application/x-tar',
        'tcap' => 'application/vnd.3gpp2.tcap',
        'tcl' => 'application/x-tcl',
        'teacher' => 'application/vnd.smart.teacher',
        'tei' => 'application/tei+xml',
        'teicorpus' => 'application/tei+xml',
        'tex' => 'application/x-tex',
        'texi' => 'application/x-texinfo',
        'texinfo' => 'application/x-texinfo',
        'text' => 'text/plain',
        'tfi' => 'application/thraud+xml',
        'tfm' => 'application/x-tex-tfm',
        'thmx' => 'application/vnd.ms-officetheme',
        'tif' => 'image/tiff',
        'tiff' => 'image/tiff',
        'tmo' => 'application/vnd.tmobile-livetv',
        'torrent' => 'application/x-bittorrent',
        'tpl' => 'application/vnd.groove-tool-template',
        'tpt' => 'application/vnd.trid.tpt',
        'tr' => 'text/troff',
        'tra' => 'application/vnd.trueapp',
        'trm' => 'application/x-msterminal',
        'tsd' => 'application/timestamped-data',
        'tsv' => 'text/tab-separated-values',
        'ttc' => 'application/x-font-ttf',
        'ttf' => 'application/x-font-ttf',
        'ttl' => 'text/turtle',
        'twd' => 'application/vnd.simtech-mindmapper',
        'twds' => 'application/vnd.simtech-mindmapper',
        'txd' => 'application/vnd.genomatix.tuxedo',
        'txf' => 'application/vnd.mobius.txf',
        'txt' => 'text/plain',
        'u32' => 'application/x-authorware-bin',
        'udeb' => 'application/x-debian-package',
        'ufd' => 'application/vnd.ufdl',
        'ufdl' => 'application/vnd.ufdl',
        'umj' => 'application/vnd.umajin',
        'unityweb' => 'application/vnd.unity',
        'uoml' => 'application/vnd.uoml+xml',
        'uri' => 'text/uri-list',
        'uris' => 'text/uri-list',
        'urls' => 'text/uri-list',
        'ustar' => 'application/x-ustar',
        'utz' => 'application/vnd.uiq.theme',
        'uu' => 'text/x-uuencode',
        'uva' => 'audio/vnd.dece.audio',
        'uvd' => 'application/vnd.dece.data',
        'uvf' => 'application/vnd.dece.data',
        'uvg' => 'image/vnd.dece.graphic',
        'uvh' => 'video/vnd.dece.hd',
        'uvi' => 'image/vnd.dece.graphic',
        'uvm' => 'video/vnd.dece.mobile',
        'uvp' => 'video/vnd.dece.pd',
        'uvs' => 'video/vnd.dece.sd',
        'uvt' => 'application/vnd.dece.ttml+xml',
        'uvu' => 'video/vnd.uvvu.mp4',
        'uvv' => 'video/vnd.dece.video',
        'uvva' => 'audio/vnd.dece.audio',
        'uvvd' => 'application/vnd.dece.data',
        'uvvf' => 'application/vnd.dece.data',
        'uvvg' => 'image/vnd.dece.graphic',
        'uvvh' => 'video/vnd.dece.hd',
        'uvvi' => 'image/vnd.dece.graphic',
        'uvvm' => 'video/vnd.dece.mobile',
        'uvvp' => 'video/vnd.dece.pd',
        'uvvs' => 'video/vnd.dece.sd',
        'uvvt' => 'application/vnd.dece.ttml+xml',
        'uvvu' => 'video/vnd.uvvu.mp4',
        'uvvv' => 'video/vnd.dece.video',
        'uvvx' => 'application/vnd.dece.unspecified',
        'uvx' => 'application/vnd.dece.unspecified',
        'vcd' => 'application/x-cdlink',
        'vcf' => 'text/x-vcard',
        'vcg' => 'application/vnd.groove-vcard',
        'vcs' => 'text/x-vcalendar',
        'vcx' => 'application/vnd.vcx',
        'vis' => 'application/vnd.visionary',
        'viv' => 'video/vnd.vivo',
        'vor' => 'application/vnd.stardivision.writer',
        'vox' => 'application/x-authorware-bin',
        'vrml' => 'model/vrml',
        'vsd' => 'application/vnd.visio',
        'vsf' => 'application/vnd.vsf',
        'vss' => 'application/vnd.visio',
        'vst' => 'application/vnd.visio',
        'vsw' => 'application/vnd.visio',
        'vtu' => 'model/vnd.vtu',
        'vxml' => 'application/voicexml+xml',
        'w3d' => 'application/x-director',
        'wad' => 'application/x-doom',
        'wav' => 'audio/x-wav',
        'wax' => 'audio/x-ms-wax',
        'wbmp' => 'image/vnd.wap.wbmp',
        'wbs' => 'application/vnd.criticaltools.wbs+xml',
        'wbxml' => 'application/vnd.wap.wbxml',
        'wcm' => 'application/vnd.ms-works',
        'wdb' => 'application/vnd.ms-works',
        'weba' => 'audio/webm',
        'webm' => 'video/webm',
        'webp' => 'image/webp',
        'wg' => 'application/vnd.pmi.widget',
        'wgt' => 'application/widget',
        'wks' => 'application/vnd.ms-works',
        'wm' => 'video/x-ms-wm',
        'wma' => 'audio/x-ms-wma',
        'wmd' => 'application/x-ms-wmd',
        'wmf' => 'application/x-msmetafile',
        'wml' => 'text/vnd.wap.wml',
        'wmlc' => 'application/vnd.wap.wmlc',
        'wmls' => 'text/vnd.wap.wmlscript',
        'wmlsc' => 'application/vnd.wap.wmlscriptc',
        'wmv' => 'video/x-ms-wmv',
        'wmx' => 'video/x-ms-wmx',
        'wmz' => 'application/x-ms-wmz',
        'woff' => 'application/x-font-woff',
        'wpd' => 'application/vnd.wordperfect',
        'wpl' => 'application/vnd.ms-wpl',
        'wps' => 'application/vnd.ms-works',
        'wqd' => 'application/vnd.wqd',
        'wri' => 'application/x-mswrite',
        'wrl' => 'model/vrml',
        'wsdl' => 'application/wsdl+xml',
        'wspolicy' => 'application/wspolicy+xml',
        'wtb' => 'application/vnd.webturbo',
        'wvx' => 'video/x-ms-wvx',
        'x32' => 'application/x-authorware-bin',
        'x3d' => 'application/vnd.hzn-3d-crossword',
        'xap' => 'application/x-silverlight-app',
        'xar' => 'application/vnd.xara',
        'xbap' => 'application/x-ms-xbap',
        'xbd' => 'application/vnd.fujixerox.docuworks.binder',
        'xbm' => 'image/x-xbitmap',
        'xdf' => 'application/xcap-diff+xml',
        'xdm' => 'application/vnd.syncml.dm+xml',
        'xdp' => 'application/vnd.adobe.xdp+xml',
        'xdssc' => 'application/dssc+xml',
        'xdw' => 'application/vnd.fujixerox.docuworks',
        'xenc' => 'application/xenc+xml',
        'xer' => 'application/patch-ops-error+xml',
        'xfdf' => 'application/vnd.adobe.xfdf',
        'xfdl' => 'application/vnd.xfdl',
        'xht' => 'application/xhtml+xml',
        'xhtml' => 'application/xhtml+xml',
        'xhvml' => 'application/xv+xml',
        'xif' => 'image/vnd.xiff',
        'xla' => 'application/vnd.ms-excel',
        'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12',
        'xlc' => 'application/vnd.ms-excel',
        'xlm' => 'application/vnd.ms-excel',
        'xls' => 'application/vnd.ms-excel',
        'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12',
        'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12',
        'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        'xlt' => 'application/vnd.ms-excel',
        'xltm' => 'application/vnd.ms-excel.template.macroenabled.12',
        'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
        'xlw' => 'application/vnd.ms-excel',
        'xml' => 'application/xml',
        'xo' => 'application/vnd.olpc-sugar',
        'xop' => 'application/xop+xml',
        'xpi' => 'application/x-xpinstall',
        'xpm' => 'image/x-xpixmap',
        'xpr' => 'application/vnd.is-xpr',
        'xps' => 'application/vnd.ms-xpsdocument',
        'xpw' => 'application/vnd.intercon.formnet',
        'xpx' => 'application/vnd.intercon.formnet',
        'xsl' => 'application/xml',
        'xslt' => 'application/xslt+xml',
        'xsm' => 'application/vnd.syncml+xml',
        'xspf' => 'application/xspf+xml',
        'xul' => 'application/vnd.mozilla.xul+xml',
        'xvm' => 'application/xv+xml',
        'xvml' => 'application/xv+xml',
        'xwd' => 'image/x-xwindowdump',
        'xyz' => 'chemical/x-xyz',
        'yaml' => 'text/yaml',
        'yang' => 'application/yang',
        'yin' => 'application/yin+xml',
        'yml' => 'text/yaml',
        'zaz' => 'application/vnd.zzazz.deck+xml',
        'zip' => 'application/zip',
        'zir' => 'application/vnd.zul',
        'zirz' => 'application/vnd.zul',
        'zmm' => 'application/vnd.handheld-entertainment+xml'
    );

    /**
     * Get a singleton instance of the class
     *
     * @return self
     * @codeCoverageIgnore
     */
    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * Get a mimetype value from a file extension
     *
     * @param string $extension File extension
     *
     * @return string|null
     *
     */
    public function fromExtension($extension)
    {
        $extension = strtolower($extension);

        return isset($this->mimetypes[$extension]) ? $this->mimetypes[$extension] : null;
    }

    /**
     * Get a mimetype from a filename
     *
     * @param string $filename Filename to generate a mimetype from
     *
     * @return string|null
     */
    public function fromFilename($filename)
    {
        return $this->fromExtension(pathinfo($filename, PATHINFO_EXTENSION));
    }
}
<?php

namespace Guzzle\Http\QueryAggregator;

use Guzzle\Http\QueryString;

/**
 * Aggregates nested query string variables using commas
 */
class CommaAggregator implements QueryAggregatorInterface
{
    public function aggregate($key, $value, QueryString $query)
    {
        if ($query->isUrlEncoding()) {
            return array($query->encodeValue($key) => implode(',', array_map(array($query, 'encodeValue'), $value)));
        } else {
            return array($key => implode(',', $value));
        }
    }
}
<?php

namespace Guzzle\Http\QueryAggregator;

use Guzzle\Http\QueryString;

/**
 * Does not aggregate nested query string values and allows duplicates in the resulting array
 *
 * Example: http://test.com?q=1&q=2
 */
class DuplicateAggregator implements QueryAggregatorInterface
{
    public function aggregate($key, $value, QueryString $query)
    {
        if ($query->isUrlEncoding()) {
            return array($query->encodeValue($key) => array_map(array($query, 'encodeValue'), $value));
        } else {
            return array($key => $value);
        }
    }
}
<?php

namespace Guzzle\Http\QueryAggregator;

use Guzzle\Http\QueryString;

/**
 * Aggregates nested query string variables using PHP style []
 */
class PhpAggregator implements QueryAggregatorInterface
{
    public function aggregate($key, $value, QueryString $query)
    {
        $ret = array();

        foreach ($value as $k => $v) {
            $k = "{$key}[{$k}]";
            if (is_array($v)) {
                $ret = array_merge($ret, self::aggregate($k, $v, $query));
            } else {
                $ret[$query->encodeValue($k)] = $query->encodeValue($v);
            }
        }

        return $ret;
    }
}
<?php

namespace Guzzle\Http\QueryAggregator;

use Guzzle\Http\QueryString;

/**
 * Interface used for aggregating nested query string variables into a flattened array of key value pairs
 */
interface QueryAggregatorInterface
{
    /**
     * Aggregate multi-valued parameters into a flattened associative array
     *
     * @param string      $key   The name of the query string parameter
     * @param array       $value The values of the parameter
     * @param QueryString $query The query string that is being aggregated
     *
     * @return array Returns an array of the combined values
     */
    public function aggregate($key, $value, QueryString $query);
}
<?php

namespace Guzzle\Http;

use Guzzle\Common\Collection;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Http\QueryAggregator\DuplicateAggregator;
use Guzzle\Http\QueryAggregator\QueryAggregatorInterface;
use Guzzle\Http\QueryAggregator\PhpAggregator;

/**
 * Query string object to handle managing query string parameters and aggregating those parameters together as a string.
 */
class QueryString extends Collection
{
    /** @var string Used to URL encode with rawurlencode */
    const RFC_3986 = 'RFC 3986';

    /** @var string Used to encode with urlencode */
    const FORM_URLENCODED = 'application/x-www-form-urlencoded';

    /** @var string Constant used to create blank query string values (e.g. ?foo) */
    const BLANK = "_guzzle_blank_";

    /** @var string The query string field separator (e.g. '&') */
    protected $fieldSeparator = '&';

    /** @var string The query string value separator (e.g. '=') */
    protected $valueSeparator = '=';

    /** @var bool URL encode fields and values */
    protected $urlEncode = 'RFC 3986';

    /** @var QueryAggregatorInterface */
    protected $aggregator;

    /** @var array Cached PHP aggregator */
    private static $defaultAggregator = null;

    /**
     * Parse a query string into a QueryString object
     *
     * @param string $query Query string to parse
     *
     * @return self
     */
    public static function fromString($query)
    {
        $q = new static();
        if ($query === '') {
            return $q;
        }

        $foundDuplicates = $foundPhpStyle = false;

        foreach (explode('&', $query) as $kvp) {
            $parts = explode('=', $kvp, 2);
            $key = rawurldecode($parts[0]);
            if ($paramIsPhpStyleArray = substr($key, -2) == '[]') {
                $foundPhpStyle = true;
                $key = substr($key, 0, -2);
            }
            if (isset($parts[1])) {
                $value = rawurldecode(str_replace('+', '%20', $parts[1]));
                if (isset($q[$key])) {
                    $q->add($key, $value);
                    $foundDuplicates = true;
                } elseif ($paramIsPhpStyleArray) {
                    $q[$key] = array($value);
                } else {
                    $q[$key] = $value;
                }
            } else {
                // Uses false by default to represent keys with no trailing "=" sign.
                $q->add($key, false);
            }
        }

        // Use the duplicate aggregator if duplicates were found and not using PHP style arrays
        if ($foundDuplicates && !$foundPhpStyle) {
            $q->setAggregator(new DuplicateAggregator());
        }

        return $q;
    }

    /**
     * Convert the query string parameters to a query string string
     *
     * @return string
     * @throws RuntimeException
     */
    public function __toString()
    {
        if (!$this->data) {
            return '';
        }

        $queryList = array();
        foreach ($this->prepareData($this->data) as $name => $value) {
            $queryList[] = $this->convertKvp($name, $value);
        }

        return implode($this->fieldSeparator, $queryList);
    }

    /**
     * Get the query string field separator
     *
     * @return string
     */
    public function getFieldSeparator()
    {
        return $this->fieldSeparator;
    }

    /**
     * Get the query string value separator
     *
     * @return string
     */
    public function getValueSeparator()
    {
        return $this->valueSeparator;
    }

    /**
     * Returns the type of URL encoding used by the query string
     *
     * One of: false, "RFC 3986", or "application/x-www-form-urlencoded"
     *
     * @return bool|string
     */
    public function getUrlEncoding()
    {
        return $this->urlEncode;
    }

    /**
     * Returns true or false if using URL encoding
     *
     * @return bool
     */
    public function isUrlEncoding()
    {
        return $this->urlEncode !== false;
    }

    /**
     * Provide a function for combining multi-valued query string parameters into a single or multiple fields
     *
     * @param null|QueryAggregatorInterface $aggregator Pass in a QueryAggregatorInterface object to handle converting
     *                                                  deeply nested query string variables into a flattened array.
     *                                                  Pass null to use the default PHP style aggregator. For legacy
     *                                                  reasons, this function accepts a callable that must accepts a
     *                                                  $key, $value, and query object.
     * @return self
     * @see \Guzzle\Http\QueryString::aggregateUsingComma()
     */
    public function setAggregator(QueryAggregatorInterface $aggregator = null)
    {
        // Use the default aggregator if none was set
        if (!$aggregator) {
            if (!self::$defaultAggregator) {
                self::$defaultAggregator = new PhpAggregator();
            }
            $aggregator = self::$defaultAggregator;
        }

        $this->aggregator = $aggregator;

        return $this;
    }

    /**
     * Set whether or not field names and values should be rawurlencoded
     *
     * @param bool|string $encode Set to TRUE to use RFC 3986 encoding (rawurlencode), false to disable encoding, or
     *                            form_urlencoding to use application/x-www-form-urlencoded encoding (urlencode)
     * @return self
     */
    public function useUrlEncoding($encode)
    {
        $this->urlEncode = ($encode === true) ? self::RFC_3986 : $encode;

        return $this;
    }

    /**
     * Set the query string separator
     *
     * @param string $separator The query string separator that will separate fields
     *
     * @return self
     */
    public function setFieldSeparator($separator)
    {
        $this->fieldSeparator = $separator;

        return $this;
    }

    /**
     * Set the query string value separator
     *
     * @param string $separator The query string separator that will separate values from fields
     *
     * @return self
     */
    public function setValueSeparator($separator)
    {
        $this->valueSeparator = $separator;

        return $this;
    }

    /**
     * Returns an array of url encoded field names and values
     *
     * @return array
     */
    public function urlEncode()
    {
        return $this->prepareData($this->data);
    }

    /**
     * URL encodes a value based on the url encoding type of the query string object
     *
     * @param string $value Value to encode
     *
     * @return string
     */
    public function encodeValue($value)
    {
        if ($this->urlEncode == self::RFC_3986) {
            return rawurlencode($value);
        } elseif ($this->urlEncode == self::FORM_URLENCODED) {
            return urlencode($value);
        } else {
            return (string) $value;
        }
    }

    /**
     * Url encode parameter data and convert nested query strings into a flattened hash.
     *
     * @param array $data The data to encode
     *
     * @return array Returns an array of encoded values and keys
     */
    protected function prepareData(array $data)
    {
        // If no aggregator is present then set the default
        if (!$this->aggregator) {
            $this->setAggregator(null);
        }

        $temp = array();
        foreach ($data as $key => $value) {
            if ($value === false || $value === null) {
                // False and null will not include the "=". Use an empty string to include the "=".
                $temp[$this->encodeValue($key)] = $value;
            } elseif (is_array($value)) {
                $temp = array_merge($temp, $this->aggregator->aggregate($key, $value, $this));
            } else {
                $temp[$this->encodeValue($key)] = $this->encodeValue($value);
            }
        }

        return $temp;
    }

    /**
     * Converts a key value pair that can contain strings, nulls, false, or arrays
     * into a single string.
     *
     * @param string $name  Name of the field
     * @param mixed  $value Value of the field
     * @return string
     */
    private function convertKvp($name, $value)
    {
        if ($value === self::BLANK || $value === null || $value === false) {
            return $name;
        } elseif (!is_array($value)) {
            return $name . $this->valueSeparator . $value;
        }

        $result = '';
        foreach ($value as $v) {
            $result .= $this->convertKvp($name, $v) . $this->fieldSeparator;
        }

        return rtrim($result, $this->fieldSeparator);
    }
}
<?php

namespace Guzzle\Http;

use Guzzle\Stream\StreamInterface;

/**
 * EntityBody decorator used to return only a subset of an entity body
 */
class ReadLimitEntityBody extends AbstractEntityBodyDecorator
{
    /** @var int Limit the number of bytes that can be read */
    protected $limit;

    /** @var int Offset to start reading from */
    protected $offset;

    /**
     * @param EntityBodyInterface $body   Body to wrap
     * @param int                 $limit  Total number of bytes to allow to be read from the stream
     * @param int                 $offset Position to seek to before reading (only works on seekable streams)
     */
    public function __construct(EntityBodyInterface $body, $limit, $offset = 0)
    {
        parent::__construct($body);
        $this->setLimit($limit)->setOffset($offset);
    }

    /**
     * Returns only a subset of the decorated entity body when cast as a string
     * {@inheritdoc}
     */
    public function __toString()
    {
        if (!$this->body->isReadable() ||
            (!$this->body->isSeekable() && $this->body->isConsumed())
        ) {
            return '';
        }

        $originalPos = $this->body->ftell();
        $this->body->seek($this->offset);
        $data = '';
        while (!$this->feof()) {
            $data .= $this->read(1048576);
        }
        $this->body->seek($originalPos);

        return (string) $data ?: '';
    }

    public function isConsumed()
    {
        return $this->body->isConsumed() ||
            ($this->body->ftell() >= $this->offset + $this->limit);
    }

    /**
     * Returns the Content-Length of the limited subset of data
     * {@inheritdoc}
     */
    public function getContentLength()
    {
        $length = $this->body->getContentLength();

        return $length === false
            ? $this->limit
            : min($this->limit, min($length, $this->offset + $this->limit) - $this->offset);
    }

    /**
     * Allow for a bounded seek on the read limited entity body
     * {@inheritdoc}
     */
    public function seek($offset, $whence = SEEK_SET)
    {
        return $whence === SEEK_SET
            ? $this->body->seek(max($this->offset, min($this->offset + $this->limit, $offset)))
            : false;
    }

    /**
     * Set the offset to start limiting from
     *
     * @param int $offset Offset to seek to and begin byte limiting from
     *
     * @return self
     */
    public function setOffset($offset)
    {
        $this->body->seek($offset);
        $this->offset = $offset;

        return $this;
    }

    /**
     * Set the limit of bytes that the decorator allows to be read from the stream
     *
     * @param int $limit Total number of bytes to allow to be read from the stream
     *
     * @return self
     */
    public function setLimit($limit)
    {
        $this->limit = $limit;

        return $this;
    }

    public function read($length)
    {
        // Check if the current position is less than the total allowed bytes + original offset
        $remaining = ($this->offset + $this->limit) - $this->body->ftell();
        if ($remaining > 0) {
            // Only return the amount of requested data, ensuring that the byte limit is not exceeded
            return $this->body->read(min($remaining, $length));
        } else {
            return false;
        }
    }
}
<?php

namespace Guzzle\Http;

use Guzzle\Common\Event;
use Guzzle\Http\Exception\BadResponseException;
use Guzzle\Http\Url;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\RequestFactory;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Exception\TooManyRedirectsException;
use Guzzle\Http\Exception\CouldNotRewindStreamException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Plugin to implement HTTP redirects. Can redirect like a web browser or using strict RFC 2616 compliance
 */
class RedirectPlugin implements EventSubscriberInterface
{
    const REDIRECT_COUNT = 'redirect.count';
    const MAX_REDIRECTS = 'redirect.max';
    const STRICT_REDIRECTS = 'redirect.strict';
    const PARENT_REQUEST = 'redirect.parent_request';
    const DISABLE = 'redirect.disable';

    /**
     * @var int Default number of redirects allowed when no setting is supplied by a request
     */
    protected $defaultMaxRedirects = 5;

    public static function getSubscribedEvents()
    {
        return array(
            'request.sent'        => array('onRequestSent', 100),
            'request.clone'       => 'cleanupRequest',
            'request.before_send' => 'cleanupRequest'
        );
    }

    /**
     * Clean up the parameters of a request when it is cloned
     *
     * @param Event $event Event emitted
     */
    public function cleanupRequest(Event $event)
    {
        $params = $event['request']->getParams();
        unset($params[self::REDIRECT_COUNT]);
        unset($params[self::PARENT_REQUEST]);
    }

    /**
     * Called when a request receives a redirect response
     *
     * @param Event $event Event emitted
     */
    public function onRequestSent(Event $event)
    {
        $response = $event['response'];
        $request = $event['request'];

        // Only act on redirect requests with Location headers
        if (!$response || $request->getParams()->get(self::DISABLE)) {
            return;
        }

        // Trace the original request based on parameter history
        $original = $this->getOriginalRequest($request);

        // Terminating condition to set the effective response on the original request
        if (!$response->isRedirect() || !$response->hasHeader('Location')) {
            if ($request !== $original) {
                // This is a terminating redirect response, so set it on the original request
                $response->getParams()->set(self::REDIRECT_COUNT, $original->getParams()->get(self::REDIRECT_COUNT));
                $original->setResponse($response);
                $response->setEffectiveUrl($request->getUrl());
            }
            return;
        }

        $this->sendRedirectRequest($original, $request, $response);
    }

    /**
     * Get the original request that initiated a series of redirects
     *
     * @param RequestInterface $request Request to get the original request from
     *
     * @return RequestInterface
     */
    protected function getOriginalRequest(RequestInterface $request)
    {
        $original = $request;
        // The number of redirects is held on the original request, so determine which request that is
        while ($parent = $original->getParams()->get(self::PARENT_REQUEST)) {
            $original = $parent;
        }

        return $original;
    }

    /**
     * Create a redirect request for a specific request object
     *
     * Takes into account strict RFC compliant redirection (e.g. redirect POST with POST) vs doing what most clients do
     * (e.g. redirect POST with GET).
     *
     * @param RequestInterface $request    Request being redirected
     * @param RequestInterface $original   Original request
     * @param int              $statusCode Status code of the redirect
     * @param string           $location   Location header of the redirect
     *
     * @return RequestInterface Returns a new redirect request
     * @throws CouldNotRewindStreamException If the body needs to be rewound but cannot
     */
    protected function createRedirectRequest(
        RequestInterface $request,
        $statusCode,
        $location,
        RequestInterface $original
    ) {
        $redirectRequest = null;
        $strict = $original->getParams()->get(self::STRICT_REDIRECTS);

        // Switch method to GET for 303 redirects.  301 and 302 redirects also switch to GET unless we are forcing RFC
        // compliance to emulate what most browsers do.  NOTE: IE only switches methods on 301/302 when coming from a POST.
        if ($request instanceof EntityEnclosingRequestInterface && ($statusCode == 303 || (!$strict && $statusCode <= 302))) {
            $redirectRequest = RequestFactory::getInstance()->cloneRequestWithMethod($request, 'GET');
        } else {
            $redirectRequest = clone $request;
        }

        $redirectRequest->setIsRedirect(true);
        // Always use the same response body when redirecting
        $redirectRequest->setResponseBody($request->getResponseBody());

        $location = Url::factory($location);
        // If the location is not absolute, then combine it with the original URL
        if (!$location->isAbsolute()) {
            $originalUrl = $redirectRequest->getUrl(true);
            // Remove query string parameters and just take what is present on the redirect Location header
            $originalUrl->getQuery()->clear();
            $location = $originalUrl->combine((string) $location, true);
        }

        $redirectRequest->setUrl($location);

        // Add the parent request to the request before it sends (make sure it's before the onRequestClone event too)
        $redirectRequest->getEventDispatcher()->addListener(
            'request.before_send',
            $func = function ($e) use (&$func, $request, $redirectRequest) {
                $redirectRequest->getEventDispatcher()->removeListener('request.before_send', $func);
                $e['request']->getParams()->set(RedirectPlugin::PARENT_REQUEST, $request);
            }
        );

        // Rewind the entity body of the request if needed
        if ($redirectRequest instanceof EntityEnclosingRequestInterface && $redirectRequest->getBody()) {
            $body = $redirectRequest->getBody();
            // Only rewind the body if some of it has been read already, and throw an exception if the rewind fails
            if ($body->ftell() && !$body->rewind()) {
                throw new CouldNotRewindStreamException(
                    'Unable to rewind the non-seekable entity body of the request after redirecting. cURL probably '
                    . 'sent part of body before the redirect occurred. Try adding acustom rewind function using on the '
                    . 'entity body of the request using setRewindFunction().'
                );
            }
        }

        return $redirectRequest;
    }

    /**
     * Prepare the request for redirection and enforce the maximum number of allowed redirects per client
     *
     * @param RequestInterface $original  Original request
     * @param RequestInterface $request   Request to prepare and validate
     * @param Response         $response  The current response
     *
     * @return RequestInterface
     */
    protected function prepareRedirection(RequestInterface $original, RequestInterface $request, Response $response)
    {
        $params = $original->getParams();
        // This is a new redirect, so increment the redirect counter
        $current = $params[self::REDIRECT_COUNT] + 1;
        $params[self::REDIRECT_COUNT] = $current;
        // Use a provided maximum value or default to a max redirect count of 5
        $max = isset($params[self::MAX_REDIRECTS]) ? $params[self::MAX_REDIRECTS] : $this->defaultMaxRedirects;

        // Throw an exception if the redirect count is exceeded
        if ($current > $max) {
            $this->throwTooManyRedirectsException($original, $max);
            return false;
        } else {
            // Create a redirect request based on the redirect rules set on the request
            return $this->createRedirectRequest(
                $request,
                $response->getStatusCode(),
                trim($response->getLocation()),
                $original
            );
        }
    }

    /**
     * Send a redirect request and handle any errors
     *
     * @param RequestInterface $original The originating request
     * @param RequestInterface $request  The current request being redirected
     * @param Response         $response The response of the current request
     *
     * @throws BadResponseException|\Exception
     */
    protected function sendRedirectRequest(RequestInterface $original, RequestInterface $request, Response $response)
    {
        // Validate and create a redirect request based on the original request and current response
        if ($redirectRequest = $this->prepareRedirection($original, $request, $response)) {
            try {
                $redirectRequest->send();
            } catch (BadResponseException $e) {
                $e->getResponse();
                if (!$e->getResponse()) {
                    throw $e;
                }
            }
        }
    }

    /**
     * Throw a too many redirects exception for a request
     *
     * @param RequestInterface $original Request
     * @param int              $max      Max allowed redirects
     *
     * @throws TooManyRedirectsException when too many redirects have been issued
     */
    protected function throwTooManyRedirectsException(RequestInterface $original, $max)
    {
        $original->getEventDispatcher()->addListener(
            'request.complete',
            $func = function ($e) use (&$func, $original, $max) {
                $original->getEventDispatcher()->removeListener('request.complete', $func);
                $str = "{$max} redirects were issued for this request:\n" . $e['request']->getRawHeaders();
                throw new TooManyRedirectsException($str);
            }
        );
    }
}
##
## Bundle of CA Root Certificates
##
## Certificate data from Mozilla downloaded on: Wed Aug 13 21:49:32 2014
##
## This is a bundle of X.509 certificates of public Certificate Authorities
## (CA). These were automatically extracted from Mozilla's root certificates
## file (certdata.txt).  This file can be found in the mozilla source tree:
## http://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt
##
## It contains the certificates in PEM format and therefore
## can be directly used with curl / libcurl / php_curl, or with
## an Apache+mod_ssl webserver for SSL client authentication.
## Just configure this file as the SSLCACertificateFile.
##
## Conversion done with mk-ca-bundle.pl verison 1.22.
## SHA1: bf2c15b3019e696660321d2227d942936dc50aa7
##


GTE CyberTrust Global Root
==========================
-----BEGIN CERTIFICATE-----
MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUg
Q29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEG
A1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEz
MjM1OTAwWjB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQL
Ex5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0
IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4u
sJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcql
HHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8FLztimQID
AQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMW
M4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OF
NMQkpw0PlZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/
-----END CERTIFICATE-----

Thawte Server CA
================
-----BEGIN CERTIFICATE-----
MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT
DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs
dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UE
AxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5j
b20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNV
BAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29u
c3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcG
A1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0
ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl
/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg7
1CcEJRCXL+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzAR
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWDTSEwjsrZqG9J
GubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6eQNuozDJ0uW8NxuOzRAvZim+aKZuZ
GCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc=
-----END CERTIFICATE-----

Thawte Premium Server CA
========================
-----BEGIN CERTIFICATE-----
MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkExFTATBgNVBAgT
DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs
dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UE
AxMYVGhhd3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZl
ckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYT
AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMU
VGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2
aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZ
cHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2
aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIh
Udib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/
qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQAm
SCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUIhfzJATj/Tb7yFkJD57taRvvBxhEf
8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7t
UCemDaYj+bvLpgcUQg==
-----END CERTIFICATE-----

Equifax Secure CA
=================
-----BEGIN CERTIFICATE-----
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE
ChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT
B0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB
nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR
fM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW
8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG
A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE
CxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG
A1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS
spXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB
Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961
zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB
BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95
70+sB3c4
-----END CERTIFICATE-----

Verisign Class 3 Public Primary Certification Authority
=======================================================
-----BEGIN CERTIFICATE-----
MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMx
FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5
IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVow
XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz
IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA
A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94
f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol
hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtMEivPLCYA
TxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59Ah
WM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2Omuf
Tqj/ZA1k
-----END CERTIFICATE-----

Verisign Class 3 Public Primary Certification Authority - G2
============================================================
-----BEGIN CERTIFICATE-----
MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVT
MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy
eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln
biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
dCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVT
MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy
eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln
biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
dCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCO
FoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71
lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQAB
MA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01UbSuvDV1Ai2TT
1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7iF6YM40AIOw7n60RzKprxaZLvcRTD
Oaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpYoJ2daZH9
-----END CERTIFICATE-----

GlobalSign Root CA
==================
-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx
GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds
b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV
BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD
VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa
DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc
THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb
Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP
c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX
gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF
AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj
Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG
j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH
hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC
X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
-----END CERTIFICATE-----

GlobalSign Root CA - R2
=======================
-----BEGIN CERTIFICATE-----
MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv
YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh
bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT
aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln
bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6
ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp
s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN
S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL
TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C
ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i
YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN
BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp
9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu
01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7
9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
-----END CERTIFICATE-----

ValiCert Class 1 VA
===================
-----BEGIN CERTIFICATE-----
MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp
b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh
bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIy
MjM0OFoXDTE5MDYyNTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0
d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEg
UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0
LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA
A4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIi
GQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCm
DuJWBQ8YTfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0LBwG
lN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLWI8sogTLDAHkY7FkX
icnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPwnXS3qT6gpf+2SQMT2iLM7XGCK5nP
Orf1LXLI
-----END CERTIFICATE-----

ValiCert Class 2 VA
===================
-----BEGIN CERTIFICATE-----
MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp
b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh
bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw
MTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0
d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIg
UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0
LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA
A4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVC
CSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7Rf
ZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZ
SWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZoDJJKPTEjlbV
UjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwCW/POuZ6lcg5Ktz885hZo+L7tdEy8
W9ViH0Pd
-----END CERTIFICATE-----

RSA Root Certificate 1
======================
-----BEGIN CERTIFICATE-----
MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp
b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh
bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw
MjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0
d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMg
UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0
LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA
A4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td
3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89H
BFx1cQqYJJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs
3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0WuPIqpsHEzXcjF
V9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/APhmcGcwTTYJBtYze4D1gCCAPRX5r
on+jjBXu
-----END CERTIFICATE-----

Verisign Class 3 Public Primary Certification Authority - G3
============================================================
-----BEGIN CERTIFICATE-----
MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV
UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw
CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv
cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg
Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1
EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc
cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw
EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj
055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f
j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0
xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa
t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
-----END CERTIFICATE-----

Verisign Class 4 Public Primary Certification Authority - G3
============================================================
-----BEGIN CERTIFICATE-----
MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV
UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw
CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv
cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkg
Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaS
tBO3IFsJ+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM
8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdLMEYH5IBtptiW
Lugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XYufTsgsbSPZUd5cBPhMnZo0QoBmrX
Razwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
j/ola09b5KROJ1WrIhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXtt
mhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm
fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c2NU8Qh0XwRJd
RTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtG
UPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg==
-----END CERTIFICATE-----

Entrust.net Secure Server CA
============================
-----BEGIN CERTIFICATE-----
MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMCVVMxFDASBgNV
BAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkg
cmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRl
ZDE6MDgGA1UEAxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhv
cml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIG
A1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBi
eSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1p
dGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0
aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQ
aO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5
gXpa0zf3wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcw
ggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHYpIHVMIHSMQsw
CQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5l
dC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF
bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENl
cnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu
dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkw
NTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0Bow
HQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EA
BAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyN
Ewr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9
n9cd2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
-----END CERTIFICATE-----

Entrust.net Premium 2048 Secure Server CA
=========================================
-----BEGIN CERTIFICATE-----
MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u
ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp
bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV
BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx
NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3
d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl
MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u
ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL
Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr
hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW
nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi
VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E
BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ
KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy
T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf
zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT
J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e
nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE=
-----END CERTIFICATE-----

Baltimore CyberTrust Root
=========================
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE
ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li
ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC
SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs
dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME
uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB
UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C
G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9
XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr
l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI
VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB
BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh
cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5
hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa
Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H
RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
-----END CERTIFICATE-----

Equifax Secure Global eBusiness CA
==================================
-----BEGIN CERTIFICATE-----
MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT
RXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNp
bmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMx
HDAaBgNVBAoTE0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEds
b2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRV
PEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzN
qfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxn
hcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j
BBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hs
MA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okEN
I7SS+RkAZ70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIY
NMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV
-----END CERTIFICATE-----

Equifax Secure eBusiness CA 1
=============================
-----BEGIN CERTIFICATE-----
MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT
RXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENB
LTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UE
ChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNz
IENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ
1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4a
IZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBk
MBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4MlIR21kW
Nl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQF
AAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5
lSE/9dR+WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+
KpYrtWKmpj29f5JZzVoqgrI3eQ==
-----END CERTIFICATE-----

AddTrust Low-Value Services Root
================================
-----BEGIN CERTIFICATE-----
MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRU
cnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQsw
CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBO
ZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY6
54eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWr
oulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1
Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJui
GMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8w
HQYDVR0OBBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTAD
AQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQswCQYDVQQGEwJT
RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEw
HwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxt
ZBsfzQ3duQH6lmM0MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph
iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY
eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJlpz/+0WatC7xr
mYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vj
ccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk=
-----END CERTIFICATE-----

AddTrust External Root
======================
-----BEGIN CERTIFICATE-----
MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD
VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw
NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU
cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg
Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821
+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw
Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo
aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy
2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7
7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P
BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL
VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk
VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB
IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl
j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355
e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u
G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
-----END CERTIFICATE-----

AddTrust Public Services Root
=============================
-----BEGIN CERTIFICATE-----
MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSAwHgYDVQQDExdBZGRU
cnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJ
BgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5l
dHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbu
nyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1i
d9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSG
Aa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAw
HM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0G
A1UdDgQWBBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkGA1UEBhMCU0Ux
FDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29yazEgMB4G
A1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4
JNojVhaTdt02KLmuG7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL
+YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao
GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh4SINhwBk/ox9
Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9H
EufOX1362KqxMy3ZdvJOOjMMK7MtkAY=
-----END CERTIFICATE-----

AddTrust Qualified Certificates Root
====================================
-----BEGIN CERTIFICATE-----
MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSMwIQYDVQQDExpBZGRU
cnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcx
CzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ
IE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx
64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3
KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1tUvznoD1o
L/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GR
wVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HU
MIHRMB0GA1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/
BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkwZzELMAkGA1UE
BhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29y
azEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQAD
ggEBABmrder4i2VhlRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG
GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X
dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3P6CxB9bpT9ze
RXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDB
iFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5noxqE=
-----END CERTIFICATE-----

Entrust Root Certification Authority
====================================
-----BEGIN CERTIFICATE-----
MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV
BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw
b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG
A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0
MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu
MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu
Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v
dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz
A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww
Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68
j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN
rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw
DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1
MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH
hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM
Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa
v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS
W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0
tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8
-----END CERTIFICATE-----

RSA Security 2048 v3
====================
-----BEGIN CERTIFICATE-----
MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6MRkwFwYDVQQK
ExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAy
MjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAb
BgNVBAsTFFJTQSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7
Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgb
WhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iH
KrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP
+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/
MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4E
FgQUB8NRMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmY
v/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5gEydxiKRz44Rj
0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+f00/FGj1EVDVwfSQpQgdMWD/YIwj
VAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395
nzIlQnQFgCi/vcEkllgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA
pKnXwiJPZ9d37CAFYd4=
-----END CERTIFICATE-----

GeoTrust Global CA
==================
-----BEGIN CERTIFICATE-----
MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw
MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j
LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo
BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet
8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc
T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU
vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD
AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk
DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q
zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4
d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2
mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p
XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm
Mw==
-----END CERTIFICATE-----

GeoTrust Global CA 2
====================
-----BEGIN CERTIFICATE-----
MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
R2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwHhcNMDQwMzA0MDUw
MDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j
LjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQDvPE1APRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/
NTL8Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hLTytCOb1k
LUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL5mkWRxHCJ1kDs6ZgwiFA
Vvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7S4wMcoKK+xfNAGw6EzywhIdLFnopsk/b
HdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNH
K266ZUapEBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6tdEPx7
srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv/NgdRN3ggX+d6Yvh
ZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywNA0ZF66D0f0hExghAzN4bcLUprbqL
OzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkC
x1YAzUm5s2x7UwQa4qjJqhIFI8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqF
H4z1Ir+rzoPz4iIprn2DQKi6bA==
-----END CERTIFICATE-----

GeoTrust Universal CA
=====================
-----BEGIN CERTIFICATE-----
MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1
MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu
Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t
JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e
RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs
7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d
8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V
qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga
Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB
Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu
KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08
ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0
XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB
hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc
aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2
qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL
oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK
xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF
KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2
DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK
xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU
p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI
P/rmMuGNG2+k5o7Y+SlIis5z/iw=
-----END CERTIFICATE-----

GeoTrust Universal CA 2
=======================
-----BEGIN CERTIFICATE-----
MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0
MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg
SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA
A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0
DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17
j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q
JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a
QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2
WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP
20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn
ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC
SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG
8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2
+/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E
BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z
dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ
4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+
mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq
A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg
Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP
pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d
FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp
gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm
X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS
-----END CERTIFICATE-----

America Online Root Certification Authority 1
=============================================
-----BEGIN CERTIFICATE-----
MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT
QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp
Y2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkG
A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg
T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lkhsmj76CG
v2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym1BW32J/X3HGrfpq/m44z
DyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsWOqMFf6Dch9Wc/HKpoH145LcxVR5lu9Rh
sCFg7RAycsWSJR74kEoYeEfffjA3PlAb2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP
8c9GsEsPPt2IYriMqQkoO3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0T
AQH/BAUwAwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAUAK3Z
o/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQB8itEf
GDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkFZu90821fnZmv9ov761KyBZiibyrF
VL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAbLjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft
3OJvx8Fi8eNy1gTIdGcL+oiroQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43g
Kd8hdIaC2y+CMMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds
sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7
-----END CERTIFICATE-----

America Online Root Certification Authority 2
=============================================
-----BEGIN CERTIFICATE-----
MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT
QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp
Y2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkG
A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg
T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQAD
ggIPADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC206B89en
fHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFciKtZHgVdEglZTvYYUAQv8
f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2JxhP7JsowtS013wMPgwr38oE18aO6lhO
qKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JN
RvCAOVIyD+OEsnpD8l7eXz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0
gBe4lL8BPeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67Xnfn
6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEqZ8A9W6Wa6897Gqid
FEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZo2C7HK2JNDJiuEMhBnIMoVxtRsX6
Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnj
B453cMor9H124HhnAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3Op
aaEg5+31IqEjFNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE
AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmnxPBUlgtk87FY
T15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2LHo1YGwRgJfMqZJS5ivmae2p
+DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzcccobGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXg
JXUjhx5c3LqdsKyzadsXg8n33gy8CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//Zoy
zH1kUQ7rVyZ2OuMeIjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgO
ZtMADjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2FAjgQ5ANh
1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUXOm/9riW99XJZZLF0Kjhf
GEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPbAZO1XB4Y3WRayhgoPmMEEf0cjQAPuDff
Z4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQlZvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuP
cX/9XhmgD0uRuMRUvAawRY8mkaKO/qk=
-----END CERTIFICATE-----

Visa eCommerce Root
===================
-----BEGIN CERTIFICATE-----
MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG
EwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug
QXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2
WhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm
VmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv
bW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL
F9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b
RaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0
TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI
/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs
GHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG
MB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc
CLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW
YFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz
zkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu
YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt
398znM/jra6O1I7mT1GvFpLgXPYHDw==
-----END CERTIFICATE-----

Certum Root CA
==============
-----BEGIN CERTIFICATE-----
MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQK
ExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQTAeFw0wMjA2MTExMDQ2Mzla
Fw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8u
by4xEjAQBgNVBAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6x
wS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdL
kKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ
89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/K
Uz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu/qpGWX7P
NSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq
hkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+
GXYkHAQaTOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvg
GrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqTE5s7FCMTY5w/
0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5xO/fIR/RpbxXyEV6DHpx8Uq79AtoS
qFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs6GAqm4VKQPNriiTsBhYscw==
-----END CERTIFICATE-----

Comodo AAA Services root
========================
-----BEGIN CERTIFICATE-----
MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw
MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl
c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG
C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs
i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW
Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH
Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK
Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f
BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl
cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz
LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm
7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z
8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C
12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
-----END CERTIFICATE-----

Comodo Secure Services root
===========================
-----BEGIN CERTIFICATE-----
MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw
MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu
Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi
BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP
9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc
rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC
oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V
p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E
FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj
YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm
aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm
4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj
Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL
DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw
pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H
RR3B7Hzs/Sk=
-----END CERTIFICATE-----

Comodo Trusted Services root
============================
-----BEGIN CERTIFICATE-----
MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw
MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h
bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw
IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7
3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y
/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6
juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS
ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud
DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp
ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl
cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw
uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32
pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA
BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l
R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O
9y5Xt5hwXsjEeLBi
-----END CERTIFICATE-----

QuoVadis Root CA
================
-----BEGIN CERTIFICATE-----
MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE
ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz
MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp
cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD
EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk
J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL
F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL
YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen
AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w
PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y
ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7
MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj
YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs
ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW
Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu
BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw
FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0
aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6
tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo
fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul
LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x
gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi
5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi
5nrQNiOKSnQ2+Q==
-----END CERTIFICATE-----

QuoVadis Root CA 2
==================
-----BEGIN CERTIFICATE-----
MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT
EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx
ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6
XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk
lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB
lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy
lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt
66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn
wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh
D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy
BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie
J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud
DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU
a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT
ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv
Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3
UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm
VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK
+JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW
IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1
WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X
f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II
4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8
VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u
-----END CERTIFICATE-----

QuoVadis Root CA 3
==================
-----BEGIN CERTIFICATE-----
MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT
EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx
OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg
DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij
KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K
DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv
BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp
p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8
nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX
MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM
Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz
uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT
BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj
YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0
aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB
BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD
VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4
ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE
AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV
qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s
hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z
POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2
Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp
8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC
bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu
g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p
vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr
qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto=
-----END CERTIFICATE-----

Security Communication Root CA
==============================
-----BEGIN CERTIFICATE-----
MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP
U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw
HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP
U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw
8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM
DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX
5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd
DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2
JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw
DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g
0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a
mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ
s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ
6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi
FL39vmwLAw==
-----END CERTIFICATE-----

Sonera Class 2 Root CA
======================
-----BEGIN CERTIFICATE-----
MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG
U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw
NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh
IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3
/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT
dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG
f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P
tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH
nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT
XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt
0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI
cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph
Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx
EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH
llpwrN9M
-----END CERTIFICATE-----

Staat der Nederlanden Root CA
=============================
-----BEGIN CERTIFICATE-----
MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJOTDEeMBwGA1UE
ChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFhdCBkZXIgTmVkZXJsYW5kZW4g
Um9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEyMTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4w
HAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxh
bmRlbiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFt
vsznExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw719tV2U02P
jLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MOhXeiD+EwR+4A5zN9RGca
C1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+UtFE5A3+y3qcym7RHjm+0Sq7lr7HcsBth
vJly3uSJt3omXdozSVtSnA71iq3DuD3oBmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn6
22r+I/q85Ej0ZytqERAhSQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRV
HSAAMDwwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMvcm9v
dC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA7Jbg0zTBLL9s+DAN
BgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k/rvuFbQvBgwp8qiSpGEN/KtcCFtR
EytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzmeafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbw
MVcoEoJz6TMvplW0C5GUR5z6u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3y
nGQI0DvDKcWy7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR
iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw==
-----END CERTIFICATE-----

TDC Internet Root CA
====================
-----BEGIN CERTIFICATE-----
MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJESzEVMBMGA1UE
ChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTAeFw0wMTA0MDUx
NjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJu
ZXQxHTAbBgNVBAsTFFREQyBJbnRlcm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAxLhAvJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20j
xsNuZp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a0vnRrEvL
znWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc14izbSysseLlJ28TQx5yc
5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGNeGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6
otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcDR0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZI
AYb4QgEBBAQDAgAHMGUGA1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMM
VERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxMEQ1JM
MTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3WjALBgNVHQ8EBAMC
AQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAwHQYDVR0OBBYEFGxkAcf9hW2syNqe
UAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJKoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0G
CSqGSIb3DQEBBQUAA4IBAQBOQ8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540m
gwV5dOy0uaOXwTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+
2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm899qNLPg7kbWzb
O0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0jUNAE4z9mQNUecYu6oah9jrU
Cbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38aQNiuJkFBT1reBK9sG9l
-----END CERTIFICATE-----

UTN DATACorp SGC Root CA
========================
-----BEGIN CERTIFICATE-----
MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UE
BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl
IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZ
BgNVBAMTElVUTiAtIERBVEFDb3JwIFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBa
MIGTMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4w
HAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRy
dXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ys
raP6LnD43m77VkIVni5c7yPeIbkFdicZD0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlo
wHDyUwDAXlCCpVZvNvlK4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA
9P4yPykqlXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulWbfXv
33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQABo4GrMIGoMAsGA1Ud
DwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRTMtGzz3/64PGgXYVOktKeRR20TzA9
BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dD
LmNybDAqBgNVHSUEIzAhBggrBgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3
DQEBBQUAA4IBAQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft
Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyjj98C5OBxOvG0
I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVHKWss5nbZqSl9Mt3JNjy9rjXx
EZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwP
DPafepE39peC4N1xaf92P2BNPM/3mfnGV/TJVTl4uix5yaaIK/QI
-----END CERTIFICATE-----

UTN USERFirst Hardware Root CA
==============================
-----BEGIN CERTIFICATE-----
MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UE
BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl
IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAd
BgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgx
OTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0
eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVz
ZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlI
wrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFd
tqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8
i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjf
Pe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkw
gbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WF
lp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNF
UkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF
BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM
//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogW
XecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2
lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kn
iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67
nfhmqA==
-----END CERTIFICATE-----

Camerfirma Chambers of Commerce Root
====================================
-----BEGIN CERTIFICATE-----
MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe
QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i
ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx
NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp
cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn
MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC
AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU
xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH
NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW
DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV
d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud
EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v
cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P
AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh
bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD
VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz
aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi
fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD
L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN
UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n
ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1
erfutGWaIZDgqtCYvDi1czyL+Nw=
-----END CERTIFICATE-----

Camerfirma Global Chambersign Root
==================================
-----BEGIN CERTIFICATE-----
MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe
QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i
ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx
NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt
YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg
MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw
ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J
1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O
by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl
6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c
8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/
BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j
aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B
Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj
aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y
ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh
bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA
PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y
gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ
PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4
IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes
t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A==
-----END CERTIFICATE-----

NetLock Notary (Class A) Root
=============================
-----BEGIN CERTIFICATE-----
MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQI
EwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6
dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9j
ayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oX
DTE5MDIxOTIzMTQ0N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQH
EwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYD
VQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFz
cyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSM
D7tM9DceqQWC2ObhbHDqeLVu0ThEDaiDzl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZ
z+qMkjvN9wfcZnSX9EUi3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC
/tmwqcm8WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7
tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCoR6
4sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCCApswDgYDVR0PAQH/BAQDAgAGMBIG
A1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaC
Ak1GSUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pv
bGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu
IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2Vn
LWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0
ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFz
IGxlaXJhc2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBh
IGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVu
b3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBh
bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sg
Q1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFp
bCBhdCBjcHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5
ayZrU3/b39/zcT0mwBQOxmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjP
ytoUMaFP0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQQeJB
CWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxkf1qbFFgBJ34TUMdr
KuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK8CtmdWOMovsEPoMOmzbwGOQmIMOM
8CgHrTwXZoi1/baI
-----END CERTIFICATE-----

NetLock Business (Class B) Root
===============================
-----BEGIN CERTIFICATE-----
MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUxETAPBgNVBAcT
CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV
BAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQDEylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikg
VGFudXNpdHZhbnlraWFkbzAeFw05OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYD
VQQGEwJIVTERMA8GA1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRv
bnNhZ2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5ldExvY2sg
VXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
iQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xKgZjupNTKihe5In+DCnVMm8Bp2GQ5o+2S
o/1bXHQawEfKOml2mrriRBf8TKPV/riXiK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr
1nGTLbO/CVRY7QbrqHvcQ7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNV
HQ8BAf8EBAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZ
RUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRh
dGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQuIEEgaGl0
ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRv
c2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUg
YXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh
c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBz
Oi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6ZXNA
bmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhl
IHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2
YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBj
cHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06sPgzTEdM
43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXan3BukxowOR0w2y7jfLKR
stE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKSNitjrFgBazMpUIaD8QFI
-----END CERTIFICATE-----

NetLock Express (Class C) Root
==============================
-----BEGIN CERTIFICATE-----
MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUxETAPBgNVBAcT
CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV
BAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQDEytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBD
KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJ
BgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6
dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMrTmV0TG9j
ayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzANBgkqhkiG9w0BAQEFAAOB
jQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNAOoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3Z
W3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63
euyucYT2BDMIJTLrdKwWRMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQw
DgYDVR0PAQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEWggJN
RklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0YWxhbm9zIFN6b2xn
YWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBB
IGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBOZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1i
aXp0b3NpdGFzYSB2ZWRpLiBBIGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0
ZWxlIGF6IGVsb2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs
ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25sYXBqYW4gYSBo
dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kga2VyaGV0byBheiBlbGxlbm9y
emVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4gSU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5k
IHRoZSB1c2Ugb2YgdGhpcyBjZXJ0aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQ
UyBhdmFpbGFibGUgYXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwg
YXQgY3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmYta3UzbM2
xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2gpO0u9f38vf5NNwgMvOOW
gyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4Fp1hBWeAyNDYpQcCNJgEjTME1A==
-----END CERTIFICATE-----

XRamp Global CA Root
====================
-----BEGIN CERTIFICATE-----
MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE
BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj
dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB
dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx
HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg
U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu
IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx
foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE
zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs
AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry
xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud
EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap
oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC
AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc
/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt
qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n
nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz
8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw=
-----END CERTIFICATE-----

Go Daddy Class 2 CA
===================
-----BEGIN CERTIFICATE-----
MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY
VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp
ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG
A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g
RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD
ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv
2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32
qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j
YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY
vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O
BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o
atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu
MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG
A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim
PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt
I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI
Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b
vZ8=
-----END CERTIFICATE-----

Starfield Class 2 CA
====================
-----BEGIN CERTIFICATE-----
MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc
U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg
Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo
MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG
A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG
SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY
bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ
JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm
epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN
F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF
MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f
hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo
bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g
QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs
afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM
PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD
KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3
QBFGmh95DmK/D5fs4C8fF5Q=
-----END CERTIFICATE-----

StartCom Certification Authority
================================
-----BEGIN CERTIFICATE-----
MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN
U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu
ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0
NjM2WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk
LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg
U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y
o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/
Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d
eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt
2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z
6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ
osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/
untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc
UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT
37uMdBNSSwIDAQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9jZXJ0LnN0YXJ0
Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0Y29tLm9yZy9zZnNj
YS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFMBgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUH
AgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRw
Oi8vY2VydC5zdGFydGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYg
U3RhcnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5
LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENl
cnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3Rh
cnRjb20ub3JnL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilT
dGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOC
AgEAFmyZ9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8jhvh
3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUWFjgKXlf2Ysd6AgXm
vB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJzewT4F+irsfMuXGRuczE6Eri8sxHk
fY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3
fsNrarnDy0RLrHiQi+fHLB5LEUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZ
EoalHmdkrQYuL6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuCO3NJo2pXh5Tl
1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6Vum0ABj6y6koQOdjQK/W/7HW/
lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkyShNOsF/5oirpt9P/FlUQqmMGqz9IgcgA38coro
g14=
-----END CERTIFICATE-----

Taiwan GRCA
===========
-----BEGIN CERTIFICATE-----
MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG
EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X
DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv
dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD
ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN
w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5
BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O
1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO
htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov
J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7
Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t
B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB
O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8
lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV
HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2
09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ
TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj
Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2
Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU
D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz
DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk
Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk
7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ
CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy
+fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS
-----END CERTIFICATE-----

Swisscom Root CA 1
==================
-----BEGIN CERTIFICATE-----
MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQG
EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy
dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4
MTgyMjA2MjBaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln
aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIIC
IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9m2BtRsiM
MW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdihFvkcxC7mlSpnzNApbjyF
NDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/TilftKaNXXsLmREDA/7n29uj/x2lzZAe
AR81sH8A25Bvxn570e56eqeqDFdvpG3FEzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkC
b6dJtDZd0KTeByy2dbcokdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn
7uHbHaBuHYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNFvJbN
cA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo19AOeCMgkckkKmUp
WyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjCL3UcPX7ape8eYIVpQtPM+GP+HkM5
haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJWbjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNY
MUJDLXT5xp6mig/p/r+D5kNXJLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw
HQYDVR0hBBYwFDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j
BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzcK6FptWfUjNP9
MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzfky9NfEBWMXrrpA9gzXrzvsMn
jgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7IkVh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQ
MbFamIp1TpBcahQq4FJHgmDmHtqBsfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4H
VtA4oJVwIHaM190e3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtl
vrsRls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ipmXeascCl
OS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HHb6D0jqTsNFFbjCYDcKF3
1QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksfrK/7DZBaZmBwXarNeNQk7shBoJMBkpxq
nvy5JMWzFYJ+vq6VK+uxwNrjAWALXmmshFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCy
x/yP2FS1k2Kdzs9Z+z0YzirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMW
NY6E0F/6MBr1mmz0DlP5OlvRHA==
-----END CERTIFICATE-----

DigiCert Assured ID Root CA
===========================
-----BEGIN CERTIFICATE-----
MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw
IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx
MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL
ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO
9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy
UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW
/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy
oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf
GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF
66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq
hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc
EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn
SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i
8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
-----END CERTIFICATE-----

DigiCert Global Root CA
=======================
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw
HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw
MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn
TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5
BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H
4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y
7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB
o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm
8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF
BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr
EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt
tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886
UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----

DigiCert High Assurance EV Root CA
==================================
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw
KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw
MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ
MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu
Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t
Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS
OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3
MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ
NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe
h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB
Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY
JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ
V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp
myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK
mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K
-----END CERTIFICATE-----

Certplus Class 2 Primary CA
===========================
-----BEGIN CERTIFICATE-----
MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE
BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN
OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy
dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR
5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ
Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO
YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e
e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME
CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ
YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t
L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD
P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R
TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+
7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW
//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7
l7+ijrRU
-----END CERTIFICATE-----

DST Root CA X3
==============
-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK
ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X
DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1
cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT
rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9
UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy
xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d
utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T
AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ
MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug
dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE
GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw
RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS
fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----

DST ACES CA X6
==============
-----BEGIN CERTIFICATE-----
MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG
EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT
MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha
MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE
CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI
DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa
pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow
GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy
MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud
EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu
Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy
dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU
CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2
5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t
Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq
nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs
vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3
oKfN5XozNmr6mis=
-----END CERTIFICATE-----

TURKTRUST Certificate Services Provider Root 1
==============================================
-----BEGIN CERTIFICATE-----
MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOcUktUUlVTVCBF
bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGDAJUUjEP
MA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykgMjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0
acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMx
MDI3MTdaFw0xNTAzMjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsg
U2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYDVQQHDAZB
TktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBC
aWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEuxZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GX
yGl8hMW0kWxsE2qkVa2kheiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8i
Si9BB35JYbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5CurKZ
8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1JuTm5Rh8i27fbMx4
W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51b0dewQIDAQABoxAwDjAMBgNVHRME
BTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46
sWrv7/hg0Uw2ZkUd82YCdAR7kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxE
q8Sn5RTOPEFhfEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy
B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdAaLX/7KfS0zgY
nNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKSRGQDJereW26fyfJOrN3H
-----END CERTIFICATE-----

TURKTRUST Certificate Services Provider Root 2
==============================================
-----BEGIN CERTIFICATE-----
MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBF
bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP
MA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg
QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcN
MDUxMTA3MTAwNzU3WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVr
dHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEPMA0G
A1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls
acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqe
LCDe2JAOCtFp0if7qnefJ1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKI
x+XlZEdhR3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJQv2g
QrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGXJHpsmxcPbe9TmJEr
5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1pzpwACPI2/z7woQ8arBT9pmAPAgMB
AAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58SFq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8G
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/ntt
Rbj2hWyfIvwqECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4
Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFzgw2lGh1uEpJ+
hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotHuFEJjOp9zYhys2AzsfAKRO8P
9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LSy3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5
UrbnBEI=
-----END CERTIFICATE-----

SwissSign Gold CA - G2
======================
-----BEGIN CERTIFICATE-----
MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw
EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN
MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp
c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B
AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq
t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C
jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg
vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF
ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR
AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend
jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO
peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR
7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi
GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw
AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64
OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov
L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm
5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr
44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf
Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m
Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp
mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk
vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf
KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br
NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj
viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ
-----END CERTIFICATE-----

SwissSign Silver CA - G2
========================
-----BEGIN CERTIFICATE-----
MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT
BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X
DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3
aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG
9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644
N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm
+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH
6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu
MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h
qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5
FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs
ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc
celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X
CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB
tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0
cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P
4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F
kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L
3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx
/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa
DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP
e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu
WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ
DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub
DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u
-----END CERTIFICATE-----

GeoTrust Primary Certification Authority
========================================
-----BEGIN CERTIFICATE-----
MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD
ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ
cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN
b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9
nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge
RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt
tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI
hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K
Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN
NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa
Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG
1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk=
-----END CERTIFICATE-----

thawte Primary Root CA
======================
-----BEGIN CERTIFICATE-----
MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE
BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2
aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv
cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3
MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg
SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv
KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT
FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs
oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ
1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc
q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K
aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p
afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF
AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE
uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89
jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH
z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA==
-----END CERTIFICATE-----

VeriSign Class 3 Public Primary Certification Authority - G5
============================================================
-----BEGIN CERTIFICATE-----
MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE
BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO
ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk
IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp
ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln
biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh
dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt
YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz
j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD
Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r
fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/
BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv
Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG
SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+
X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE
KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC
Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE
ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
-----END CERTIFICATE-----

SecureTrust CA
==============
-----BEGIN CERTIFICATE-----
MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG
EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy
dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe
BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX
OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t
DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH
GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b
01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH
ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/
BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj
aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ
KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu
SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf
mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ
nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
-----END CERTIFICATE-----

Secure Global CA
================
-----BEGIN CERTIFICATE-----
MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG
EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH
bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg
MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg
Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx
YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ
bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g
8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV
HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi
0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud
EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn
oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA
MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+
OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn
CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5
3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc
f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW
-----END CERTIFICATE-----

COMODO Certification Authority
==============================
-----BEGIN CERTIFICATE-----
MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE
BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG
A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1
dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb
MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD
T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH
+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww
xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV
4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA
1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI
rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E
BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k
b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC
AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP
OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc
IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN
+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ==
-----END CERTIFICATE-----

Network Solutions Certificate Authority
=======================================
-----BEGIN CERTIFICATE-----
MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG
EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr
IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx
MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx
jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT
aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT
crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc
/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB
AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP
BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv
bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA
A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q
4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/
GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv
wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD
ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
-----END CERTIFICATE-----

WellsSecure Public Root Certificate Authority
=============================================
-----BEGIN CERTIFICATE-----
MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM
F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw
NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN
MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl
bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD
VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1
iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13
i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8
bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB
K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB
AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu
cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm
lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB
i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww
GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg
Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI
K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0
bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj
qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es
E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ
tylv2G0xffX8oRAHh84vWdw+WNs=
-----END CERTIFICATE-----

COMODO ECC Certification Authority
==================================
-----BEGIN CERTIFICATE-----
MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC
R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE
ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB
dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix
GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo
b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X
4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni
wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E
BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG
FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA
U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
-----END CERTIFICATE-----

IGC/A
=====
-----BEGIN CERTIFICATE-----
MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYD
VQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVE
Q1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZy
MB4XDTAyMTIxMzE0MjkyM1oXDTIwMTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQI
EwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NT
STEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaIs9z4iPf930Pfeo2aSVz2
TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCW
So7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYy
HF2fYPepraX/z9E0+X1bF8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNd
frGoRpAxVs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGdPDPQ
tQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNVHSAEDjAMMAoGCCqB
egF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAxNjAfBgNVHSMEGDAWgBSjBS8YYFDC
iQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUFAAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RK
q89toB9RlPhJy3Q2FLwV3duJL92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3Q
MZsyK10XZZOYYLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg
Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2aNjSaTFR+FwNI
lQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R0982gaEbeC9xs/FZTEYYKKuF
0mBWWg==
-----END CERTIFICATE-----

Security Communication EV RootCA1
=================================
-----BEGIN CERTIFICATE-----
MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh
dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE
BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl
Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO
/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX
WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z
ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4
bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK
9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm
iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG
Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW
mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW
T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490
-----END CERTIFICATE-----

OISTE WISeKey Global Root GA CA
===============================
-----BEGIN CERTIFICATE-----
MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE
BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG
A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH
bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD
VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw
IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5
IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9
Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg
Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD
d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ
/yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R
LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw
AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ
KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm
MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4
+vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa
hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY
okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0=
-----END CERTIFICATE-----

Microsec e-Szigno Root CA
=========================
-----BEGIN CERTIFICATE-----
MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE
BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL
EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0
MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz
dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT
GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG
d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N
oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc
QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ
PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb
MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG
IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD
VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3
LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A
dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn
AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA
4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg
AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA
egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6
Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO
PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv
c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h
cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw
IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT
WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV
MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER
MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp
Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal
HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT
nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE
aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a
86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK
yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB
S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU=
-----END CERTIFICATE-----

Certigna
========
-----BEGIN CERTIFICATE-----
MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw
EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3
MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI
Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q
XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH
GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p
ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg
DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf
Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ
tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ
BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J
SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA
hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+
ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu
PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY
1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw
WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==
-----END CERTIFICATE-----

AC Ra\xC3\xADz Certic\xC3\xA1mara S.A.
======================================
-----BEGIN CERTIFICATE-----
MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsxCzAJBgNVBAYT
AkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRpZmljYWNpw7NuIERpZ2l0YWwg
LSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwaQUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4w
HhcNMDYxMTI3MjA0NjI5WhcNMzAwNDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+
U29jaWVkYWQgQ2FtZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJh
IFMuQS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkqhkiG9w0B
AQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeGqentLhM0R7LQcNzJPNCN
yu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzLfDe3fezTf3MZsGqy2IiKLUV0qPezuMDU
2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQY5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU3
4ojC2I+GdV75LaeHM/J4Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP
2yYe68yQ54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+bMMCm
8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48jilSH5L887uvDdUhf
HjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++EjYfDIJss2yKHzMI+ko6Kh3VOz3vCa
Mh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/ztA/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK
5lw1omdMEWux+IBkAC1vImHFrEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1b
czwmPS9KvqfJpxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE
AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCBlTCBkgYEVR0g
ADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFyYS5jb20vZHBjLzBaBggrBgEF
BQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW507WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2Ug
cHVlZGVuIGVuY29udHJhciBlbiBsYSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEf
AygPU3zmpFmps4p6xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuX
EpBcunvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/Jre7Ir5v
/zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dpezy4ydV/NgIlqmjCMRW3
MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42gzmRkBDI8ck1fj+404HGIGQatlDCIaR4
3NAvO2STdPCWkPHv+wlaNECW8DYSwaN0jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wk
eZBWN7PGKX6jD/EpOe9+XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f
/RWmnkJDW2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/RL5h
RqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35rMDOhYil/SrnhLecU
Iw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxkBYn8eNZcLCZDqQ==
-----END CERTIFICATE-----

TC TrustCenter Class 2 CA II
============================
-----BEGIN CERTIFICATE-----
MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC
REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy
IENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYw
MTEyMTQzODQzWhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1
c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UE
AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jftMjWQ+nEdVl//OEd+DFw
IxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKguNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2
xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2JXjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQ
Xa7pIXSSTYtZgo+U4+lK8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7u
SNQZu+995OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1UdEwEB
/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3kUrL84J6E1wIqzCB
7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90
Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU
cnVzdENlbnRlciUyMENsYXNzJTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i
SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u
TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iSGNn3Bzn1LL4G
dXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprtZjluS5TmVfwLG4t3wVMTZonZ
KNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8au0WOB9/WIFaGusyiC2y8zl3gK9etmF1Kdsj
TYjKUCjLhdLTEKJZbtOTVAB6okaVhgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kP
JOzHdiEoZa5X6AeIdUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfk
vQ==
-----END CERTIFICATE-----

TC TrustCenter Class 3 CA II
============================
-----BEGIN CERTIFICATE-----
MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC
REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy
IENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYw
MTEyMTQ0MTU3WhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1
c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UE
AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJWHt4bNwcwIi9v8Qbxq63W
yKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+QVl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo
6SI7dYnWRBpl8huXJh0obazovVkdKyT21oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZ
uV3bOx4a+9P/FRQI2AlqukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk
2ZyqBwi1Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1UdEwEB
/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NXXAek0CSnwPIA1DCB
7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90
Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU
cnVzdENlbnRlciUyMENsYXNzJTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i
SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u
TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlNirTzwppVMXzE
O2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8TtXqluJucsG7Kv5sbviRmEb8
yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9
IJqDnxrcOfHFcqMRA/07QlIp2+gB95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal
092Y+tTmBvTwtiBjS+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc
5A==
-----END CERTIFICATE-----

TC TrustCenter Universal CA I
=============================
-----BEGIN CERTIFICATE-----
MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTELMAkGA1UEBhMC
REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy
IFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcN
MDYwMzIyMTU1NDI4WhcNMjUxMjMxMjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMg
VHJ1c3RDZW50ZXIgR21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYw
JAYDVQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSRJJZ4Hgmgm5qVSkr1YnwC
qMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3TfCZdzHd55yx4Oagmcw6iXSVphU9VDprv
xrlE4Vc93x9UIuVvZaozhDrzznq+VZeujRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtw
ag+1m7Z3W0hZneTvWq3zwZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9O
gdwZu5GQfezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYDVR0j
BBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
AYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0GCSqGSIb3DQEBBQUAA4IBAQAo0uCG
1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X17caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/Cy
vwbZ71q+s2IhtNerNXxTPqYn8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3
ghUJGooWMNjsydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT
ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/2TYcuiUaUj0a
7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY
-----END CERTIFICATE-----

Deutsche Telekom Root CA 2
==========================
-----BEGIN CERTIFICATE-----
MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT
RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG
A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5
MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G
A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS
b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5
bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI
KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY
AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK
Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV
jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV
HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr
E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy
zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8
rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G
dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU
Cm26OWMohpLzGITY+9HPBVZkVw==
-----END CERTIFICATE-----

ComSign Secured CA
==================
-----BEGIN CERTIFICATE-----
MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAwPDEbMBkGA1UE
AxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQGEwJJTDAeFw0w
NDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBD
QTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQDGtWhfHZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs
49ohgHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sWv+bznkqH
7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ueMv5WJDmyVIRD9YTC2LxB
kMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d1
9guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUw
AwEB/zBEBgNVHR8EPTA7MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29t
U2lnblNlY3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58ADsA
j8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkqhkiG9w0BAQUFAAOC
AQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7piL1DRYHjZiM/EoZNGeQFsOY3wo3a
BijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtCdsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtp
FhpFfTMDZflScZAmlaxMDPWLkz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP
51qJThRv4zdLhfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz
OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw==
-----END CERTIFICATE-----

Cybertrust Global Root
======================
-----BEGIN CERTIFICATE-----
MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li
ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4
MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD
ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW
0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL
AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin
89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT
8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2
MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G
A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO
lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi
5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2
hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T
X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW
WL1WMRJOEcgh4LMRkWXbtKaIOM5V
-----END CERTIFICATE-----

ePKI Root Certification Authority
=================================
-----BEGIN CERTIFICATE-----
MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG
EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg
Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx
MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq
MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B
AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs
IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi
lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv
qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX
12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O
WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+
ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao
lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/
vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi
Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi
MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH
ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0
1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq
KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV
xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP
NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r
GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE
xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx
gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy
sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD
BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw=
-----END CERTIFICATE-----

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
=============================================================================================================================
-----BEGIN CERTIFICATE-----
MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRSMRgwFgYDVQQH
DA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJpbGltc2VsIHZlIFRla25vbG9q
aWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSwVEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ry
b25payB2ZSBLcmlwdG9sb2ppIEFyYcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNV
BAsMGkthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUg
S8O2ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAeFw0wNzA4
MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIxGDAWBgNVBAcMD0dlYnpl
IC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmlsaW1zZWwgdmUgVGVrbm9sb2ppayBBcmHF
n3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBUQUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZl
IEtyaXB0b2xvamkgQXJhxZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2Ft
dSBTZXJ0aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7ZrIFNl
cnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4hgb46ezzb8R1Sf1n68yJMlaCQvEhO
Eav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yKO7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1
xnnRFDDtG1hba+818qEhTsXOfJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR
6Oqeyjh1jmKwlZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL
hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQIDAQABo0IwQDAd
BgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmPNOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4
N5EY3ATIZJkrGG2AA1nJrvhY0D7twyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLT
y9LQQfMmNkqblWwM7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYh
LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M
dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI=
-----END CERTIFICATE-----

Buypass Class 2 CA 1
====================
-----BEGIN CERTIFICATE-----
MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMiBDQSAxMB4XDTA2
MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh
c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7M
cXA0ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLXl18xoS83
0r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVBHfCuuCkslFJgNJQ72uA4
0Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/R
uFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNC
MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0P
AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLPgcIV
1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+DKhQ7SLHrQVMdvvt
7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKuBctN518fV4bVIJwo+28TOPX2EZL2
fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w
wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho
-----END CERTIFICATE-----

Buypass Class 3 CA 1
====================
-----BEGIN CERTIFICATE-----
MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMyBDQSAxMB4XDTA1
MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh
c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKx
ifZgisRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//zNIqeKNc0
n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI+MkcVyzwPX6UvCWThOia
AJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2RhzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c
1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNC
MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0P
AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFPBdy7
pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27sEzNxZy5p+qksP2bA
EllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2mSlf56oBzKwzqBwKu5HEA6BvtjT5
htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yCe/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQj
el/wroQk5PMr+4okoyeYZdowdXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915
-----END CERTIFICATE-----

EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1
==========================================================================
-----BEGIN CERTIFICATE-----
MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNVBAMML0VCRyBF
bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMTcwNQYDVQQKDC5FQkcg
QmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXptZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAe
Fw0wNjA4MTcwMDIxMDlaFw0xNjA4MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25p
ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2lt
IFRla25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h4fuXd7hxlugTlkaDT7by
X3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAktiHq6yOU/im/+4mRDGSaBUorzAzu8T2b
gmmkTPiab+ci2hC6X5L8GCcKqKpE+i4stPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfr
eYteIAbTdgtsApWjluTLdlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZ
TqNGFav4c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8UmTDGy
Y5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z+kI2sSXFCjEmN1Zn
uqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0OLna9XvNRiYuoP1Vzv9s6xiQFlpJI
qkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMWOeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vm
ExH8nYQKE3vwO9D8owrXieqWfo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0
Nokb+Clsi7n2l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB
/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgwFoAU587GT/wW
Z5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+8ygjdsZs93/mQJ7ANtyVDR2t
FcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgm
zJNSroIBk5DKd8pNSe/iWtkqvTDOTLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64k
XPBfrAowzIpAoHMEwfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqT
bCmYIai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJnxk1Gj7sU
RT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4QDgZxGhBM/nV+/x5XOULK
1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9qKd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt
2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11thie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQ
Y9iJSrSq3RZj9W6+YKH47ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9
AahH3eU7QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT
-----END CERTIFICATE-----

certSIGN ROOT CA
================
-----BEGIN CERTIFICATE-----
MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD
VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa
Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE
CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I
JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH
rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2
ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD
0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943
AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B
Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB
AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8
SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0
x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt
vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz
TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD
-----END CERTIFICATE-----

CNNIC ROOT
==========
-----BEGIN CERTIFICATE-----
MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJDTjEOMAwGA1UE
ChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2MDcwOTE0WhcNMjcwNDE2MDcw
OTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1Qw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzD
o+/hn7E7SIX1mlwhIhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tiz
VHa6dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZOV/kbZKKT
VrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrCGHn2emU1z5DrvTOTn1Or
czvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gNv7Sg2Ca+I19zN38m5pIEo3/PIKe38zrK
y5nLAgMBAAGjczBxMBEGCWCGSAGG+EIBAQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscC
wQ7vptU7ETAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991S
lgrHAsEO76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnKOOK5
Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvHugDnuL8BV8F3RTIM
O/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7HgviyJA/qIYM/PmLXoXLT1tLYhFHxUV8
BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fLbuXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2
G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m
mxE=
-----END CERTIFICATE-----

ApplicationCA - Japanese Government
===================================
-----BEGIN CERTIFICATE-----
MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT
SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw
MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl
cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4
fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN
wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE
jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu
nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU
WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV
BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD
vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs
o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g
/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD
io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW
dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL
rosot4LKGAfmt1t06SAZf7IbiVQ=
-----END CERTIFICATE-----

GeoTrust Primary Certification Authority - G3
=============================================
-----BEGIN CERTIFICATE-----
MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE
BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0
IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy
eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz
NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo
YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT
LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j
K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE
c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C
IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu
dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC
MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr
2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9
cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE
Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD
AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s
t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt
-----END CERTIFICATE-----

thawte Primary Root CA - G2
===========================
-----BEGIN CERTIFICATE-----
MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC
VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu
IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg
Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV
MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG
b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt
IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS
LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5
8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU
mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN
G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K
rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg==
-----END CERTIFICATE-----

thawte Primary Root CA - G3
===========================
-----BEGIN CERTIFICATE-----
MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE
BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2
aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv
cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w
ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh
d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD
VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG
A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At
P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC
+BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY
7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW
vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E
BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ
KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK
A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu
t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC
8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm
er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A=
-----END CERTIFICATE-----

GeoTrust Primary Certification Authority - G2
=============================================
-----BEGIN CERTIFICATE-----
MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC
VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu
Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD
ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1
OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg
MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl
b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG
BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc
KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD
VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+
EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m
ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2
npaqBA+K
-----END CERTIFICATE-----

VeriSign Universal Root Certification Authority
===============================================
-----BEGIN CERTIFICATE-----
MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE
BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO
ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk
IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u
IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV
UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0
aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj
1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP
MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72
9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I
AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR
tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G
CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O
a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud
DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3
Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx
Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx
P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P
wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4
mJO37M2CYfE45k+XmCpajQ==
-----END CERTIFICATE-----

VeriSign Class 3 Public Primary Certification Authority - G4
============================================================
-----BEGIN CERTIFICATE-----
MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC
VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3
b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz
ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU
cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo
b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5
IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8
Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz
rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB
/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw
HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u
Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD
A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx
AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA==
-----END CERTIFICATE-----

NetLock Arany (Class Gold) Főtanúsítvány
============================================
-----BEGIN CERTIFICATE-----
MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G
A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610
dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB
cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx
MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO
ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv
biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6
c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu
0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw
/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk
H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw
fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1
neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB
BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW
qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta
YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC
bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna
NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu
dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
-----END CERTIFICATE-----

Staat der Nederlanden Root CA - G2
==================================
-----BEGIN CERTIFICATE-----
MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE
CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g
Um9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oXDTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMC
TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l
ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ
5291qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8SpuOUfiUtn
vWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPUZ5uW6M7XxgpT0GtJlvOj
CwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvEpMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiil
e7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCR
OME4HYYEhLoaJXhena/MUGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpI
CT0ugpTNGmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy5V65
48r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv6q012iDTiIJh8BIi
trzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEKeN5KzlW/HdXZt1bv8Hb/C3m1r737
qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMB
AAGjgZcwgZQwDwYDVR0TAQH/BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcC
ARYxaHR0cDovL3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqGSIb3DQEBCwUA
A4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLySCZa59sCrI2AGeYwRTlHSeYAz
+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwj
f/ST7ZwaUb7dRUG/kSS0H4zpX897IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaN
kqbG9AclVMwWVxJKgnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfk
CpYL+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxLvJxxcypF
URmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkmbEgeqmiSBeGCc1qb3Adb
CG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvkN1trSt8sV4pAWja63XVECDdCcAz+3F4h
oKOKwJCcaNpQ5kUQR3i2TtJlycM33+FCY7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoV
IPVVYpbtbZNQvOSqeK3Zywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm
66+KAQ==
-----END CERTIFICATE-----

CA Disig
========
-----BEGIN CERTIFICATE-----
MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMK
QnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwHhcNMDYw
MzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlz
bGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgm
GErENx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnXmjxUizkD
Pw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYDXcDtab86wYqg6I7ZuUUo
hwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhWS8+2rT+MitcE5eN4TPWGqvWP+j1scaMt
ymfraHtuM6kMgiioTGohQBUgDCZbg8KpFhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8w
gfwwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0P
AQH/BAQDAgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cuZGlz
aWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5zay9jYS9jcmwvY2Ff
ZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2svY2EvY3JsL2NhX2Rpc2lnLmNybDAa
BgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEwDQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59t
WDYcPQuBDRIrRhCA/ec8J9B6yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3
mkkp7M5+cTxqEEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/
CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeBEicTXxChds6K
ezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFNPGO+I++MzVpQuGhU+QqZMxEA
4Z7CRneC9VkGjCFMhwnN5ag=
-----END CERTIFICATE-----

Juur-SK
=======
-----BEGIN CERTIFICATE-----
MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglwa2lA
c2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMRAw
DgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMwMVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqG
SIb3DQEJARYJcGtpQHNrLmVlMQswCQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVy
aW1pc2tlc2t1czEQMA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOBSvZiF3tf
TQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkzABpTpyHhOEvWgxutr2TC
+Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvHLCu3GFH+4Hv2qEivbDtPL+/40UceJlfw
UR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMPPbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDa
Tpxt4brNj3pssAki14sL2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQF
MAMBAf8wggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwICMIHD
HoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDkAGwAagBhAHMAdABh
AHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0AHMAZQBlAHIAaQBtAGkAcwBrAGUA
cwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABzAGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABr
AGkAbgBuAGkAdABhAG0AaQBzAGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nw
cy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE
FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcYP2/v6X2+MA4G
A1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOiCfP+JmeaUOTDBS8rNXiRTHyo
ERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+gkcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyL
abVAyJRld/JXIWY7zoVAtjNjGr95HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678
IIbsSt4beDI3poHSna9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkh
Mp6qqIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0ZTbvGRNs2
yyqcjg==
-----END CERTIFICATE-----

Hongkong Post Root CA 1
=======================
-----BEGIN CERTIFICATE-----
MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT
DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx
NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n
IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1
ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr
auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh
qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY
V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV
HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i
h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio
l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei
IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps
T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT
c4afU9hDDl3WY4JxHYB0yvbiAmvZWg==
-----END CERTIFICATE-----

SecureSign RootCA11
===================
-----BEGIN CERTIFICATE-----
MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi
SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS
b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw
KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1
cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL
TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO
wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq
g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP
O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA
bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX
t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh
OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r
bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ
Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01
y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061
lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I=
-----END CERTIFICATE-----

ACEDICOM Root
=============
-----BEGIN CERTIFICATE-----
MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UEAwwNQUNFRElD
T00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMB4XDTA4
MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEWMBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoG
A1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHk
WLn709gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7XBZXehuD
YAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5PGrjm6gSSrj0RuVFCPYew
MYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAKt0SdE3QrwqXrIhWYENiLxQSfHY9g5QYb
m8+5eaA9oiM/Qj9r+hwDezCNzmzAv+YbX79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbk
HQl/Sog4P75n/TSW9R28MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTT
xKJxqvQUfecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI2Sf2
3EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyHK9caUPgn6C9D4zq9
2Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEaeZAwUswdbxcJzbPEHXEUkFDWug/Fq
TYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz
4SsrSbbXc6GqlPUB53NlTKxQMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU
9QHnc2VMrFAwRAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv
bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWImfQwng4/F9tqg
aHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3gvoFNTPhNahXwOf9jU8/kzJP
eGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKeI6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1Pwk
zQSulgUV1qzOMPPKC8W64iLgpq0i5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1
ThCojz2GuHURwCRiipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oI
KiMnMCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZo5NjEFIq
nxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6zqylfDJKZ0DcMDQj3dcE
I2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacNGHk0vFQYXlPKNFHtRQrmjseCNj6nOGOp
MCwXEGCSn1WHElkQwg9naRHMTh5+Spqtr0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3o
tkYNbn5XOmeUwssfnHdKZ05phkOTOPu220+DkdRgfks+KzgHVZhepA==
-----END CERTIFICATE-----

Verisign Class 3 Public Primary Certification Authority
=======================================================
-----BEGIN CERTIFICATE-----
MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMx
FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5
IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVow
XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz
IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA
A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94
f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol
hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBABByUqkFFBky
CEHwxWsKzH4PIRnN5GfcX6kb5sroc50i2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWX
bj9T/UWZYB2oK0z5XqcJ2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/
D/xwzoiQ
-----END CERTIFICATE-----

Microsec e-Szigno Root CA 2009
==============================
-----BEGIN CERTIFICATE-----
MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER
MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv
c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o
dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE
BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt
U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA
fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG
0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA
pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm
1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC
AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf
QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE
FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o
lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX
I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775
tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02
yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi
LXpUq3DDfSJlgnCW
-----END CERTIFICATE-----

E-Guven Kok Elektronik Sertifika Hizmet Saglayicisi
===================================================
-----BEGIN CERTIFICATE-----
MIIDtjCCAp6gAwIBAgIQRJmNPMADJ72cdpW56tustTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG
EwJUUjEoMCYGA1UEChMfRWxla3Ryb25payBCaWxnaSBHdXZlbmxpZ2kgQS5TLjE8MDoGA1UEAxMz
ZS1HdXZlbiBLb2sgRWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhZ2xheWljaXNpMB4XDTA3
MDEwNDExMzI0OFoXDTE3MDEwNDExMzI0OFowdTELMAkGA1UEBhMCVFIxKDAmBgNVBAoTH0VsZWt0
cm9uaWsgQmlsZ2kgR3V2ZW5saWdpIEEuUy4xPDA6BgNVBAMTM2UtR3V2ZW4gS29rIEVsZWt0cm9u
aWsgU2VydGlmaWthIEhpem1ldCBTYWdsYXlpY2lzaTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAMMSIJ6wXgBljU5Gu4Bc6SwGl9XzcslwuedLZYDBS75+PNdUMZTe1RK6UxYC6lhj71vY
8+0qGqpxSKPcEC1fX+tcS5yWCEIlKBHMilpiAVDV6wlTL/jDj/6z/P2douNffb7tC+Bg62nsM+3Y
jfsSSYMAyYuXjDtzKjKzEve5TfL0TW3H5tYmNwjy2f1rXKPlSFxYvEK+A1qBuhw1DADT9SN+cTAI
JjjcJRFHLfO6IxClv7wC90Nex/6wN1CZew+TzuZDLMN+DfIcQ2Zgy2ExR4ejT669VmxMvLz4Bcpk
9Ok0oSy1c+HCPujIyTQlCFzz7abHlJ+tiEMl1+E5YP6sOVkCAwEAAaNCMEAwDgYDVR0PAQH/BAQD
AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJ/uRLOU1fqRTy7ZVZoEVtstxNulMA0GCSqG
SIb3DQEBBQUAA4IBAQB/X7lTW2M9dTLn+sR0GstG30ZpHFLPqk/CaOv/gKlR6D1id4k9CnU58W5d
F4dvaAXBlGzZXd/aslnLpRCKysw5zZ/rTt5S/wzw9JKp8mxTq5vSR6AfdPebmvEvFZ96ZDAYBzwq
D2fK/A+JYZ1lpTzlvBNbCNvj/+27BrtqBrF6T2XGgv0enIu1De5Iu7i9qgi0+6N8y5/NkHZchpZ4
Vwpm+Vganf2XKWDeEaaQHBkc7gGWIjQ0LpH5t8Qn0Xvmv/uARFoW5evg1Ao4vOSR49XrXMGs3xtq
fJ7lddK2l4fbzIcrQzqECK+rPNv3PGYxhrCdU3nt+CPeQuMtgvEP5fqX
-----END CERTIFICATE-----

GlobalSign Root CA - R3
=======================
-----BEGIN CERTIFICATE-----
MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv
YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh
bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT
aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln
bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt
iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ
0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3
rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl
OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2
xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7
lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8
EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E
bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18
YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r
kpeDMdmztcpHWD9f
-----END CERTIFICATE-----

Autoridad de Certificacion Firmaprofesional CIF A62634068
=========================================================
-----BEGIN CERTIFICATE-----
MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA
BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2
MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw
QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB
NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD
Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P
B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY
7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH
ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI
plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX
MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX
LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK
bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU
vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud
EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH
DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp
cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA
bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx
ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx
51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk
R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP
T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f
Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl
osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR
crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR
saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD
KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi
6Et8Vcad+qMUu2WFbm5PEn4KPJ2V
-----END CERTIFICATE-----

Izenpe.com
==========
-----BEGIN CERTIFICATE-----
MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG
EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz
MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu
QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ
03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK
ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU
+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC
PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT
OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK
F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK
0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+
0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB
leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID
AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+
SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG
NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx
MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O
BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l
Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga
kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q
hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs
g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5
aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5
nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC
ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo
Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z
WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==
-----END CERTIFICATE-----

Chambers of Commerce Root - 2008
================================
-----BEGIN CERTIFICATE-----
MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD
MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv
bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu
QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy
Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl
ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF
EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl
cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA
XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj
h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/
ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk
NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g
D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331
lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ
0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj
ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2
EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI
G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ
BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh
bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh
bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC
CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH
AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1
wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH
3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU
RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6
M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1
YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF
9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK
zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG
nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg
OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ
-----END CERTIFICATE-----

Global Chambersign Root - 2008
==============================
-----BEGIN CERTIFICATE-----
MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD
MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv
bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu
QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx
NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg
Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ
QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD
aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf
VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf
XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0
ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB
/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA
TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M
H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe
Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF
HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh
wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB
AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT
BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE
BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm
aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm
aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp
1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0
dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG
/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6
ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s
dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg
9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH
foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du
qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr
P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq
c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z
09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B
-----END CERTIFICATE-----

Go Daddy Root Certificate Authority - G2
========================================
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu
MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5
MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G
A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq
9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD
+qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd
fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl
NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC
MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9
BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac
vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r
5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV
N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO
LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1
-----END CERTIFICATE-----

Starfield Root Certificate Authority - G2
=========================================
-----BEGIN CERTIFICATE-----
MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s
b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0
eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw
DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg
VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB
dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv
W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs
bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk
N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf
ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU
JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol
TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx
4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw
F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ
c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
-----END CERTIFICATE-----

Starfield Services Root Certificate Authority - G2
==================================================
-----BEGIN CERTIFICATE-----
MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s
b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl
IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV
BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT
dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg
Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2
h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa
hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP
LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB
rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw
AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG
SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP
E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy
xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd
iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza
YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6
-----END CERTIFICATE-----

AffirmTrust Commercial
======================
-----BEGIN CERTIFICATE-----
MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS
BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw
MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly
bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb
DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV
C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6
BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww
MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV
HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG
hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi
qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv
0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh
sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=
-----END CERTIFICATE-----

AffirmTrust Networking
======================
-----BEGIN CERTIFICATE-----
MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS
BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw
MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly
bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE
Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI
dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24
/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb
h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV
HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu
UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6
12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23
WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9
/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=
-----END CERTIFICATE-----

AffirmTrust Premium
===================
-----BEGIN CERTIFICATE-----
MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS
BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy
OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy
dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn
BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV
5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs
+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd
GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R
p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI
S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04
6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5
/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo
+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB
/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv
MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg
Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC
6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S
L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK
+4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV
BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg
IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60
g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb
zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw==
-----END CERTIFICATE-----

AffirmTrust Premium ECC
=======================
-----BEGIN CERTIFICATE-----
MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV
BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx
MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U
cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA
IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ
N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW
BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK
BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X
57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM
eQ==
-----END CERTIFICATE-----

Certum Trusted Network CA
=========================
-----BEGIN CERTIFICATE-----
MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK
ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv
biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy
MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU
ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC
l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J
J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4
fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0
cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB
Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw
DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj
jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1
mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj
Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=
-----END CERTIFICATE-----

Certinomis - Autorité Racine
=============================
-----BEGIN CERTIFICATE-----
MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK
Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg
LSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkG
A1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYw
JAYDVQQDDB1DZXJ0aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQAD
ggIPADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jYF1AMnmHa
wE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N8y4oH3DfVS9O7cdxbwly
Lu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWerP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw
2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92N
jMD2AR5vpTESOH2VwnHu7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9q
c1pkIuVC28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6lSTC
lrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1Enn1So2+WLhl+HPNb
xxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB0iSVL1N6aaLwD4ZFjliCK0wi1F6g
530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql095gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna
4NH4+ej9Uji29YnfAgMBAAGjWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
A1UdDgQWBBQNjLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ
KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9sov3/4gbIOZ/x
WqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZMOH8oMDX/nyNTt7buFHAAQCva
R6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40
nJ+U8/aGH88bc62UeYdocMMzpXDn2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1B
CxMjidPJC+iKunqjo3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjv
JL1vnxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG5ERQL1TE
qkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWqpdEdnV1j6CTmNhTih60b
WfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZbdsLLO7XSAPCjDuGtbkD326C00EauFddE
wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/
vgt2Fl43N+bYdJeimUV5
-----END CERTIFICATE-----

Root CA Generalitat Valenciana
==============================
-----BEGIN CERTIFICATE-----
MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJFUzEfMB0GA1UE
ChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290
IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcNMDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3
WjBoMQswCQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UE
CxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+WmmmO3I2
F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKjSgbwJ/BXufjpTjJ3Cj9B
ZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGlu6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQ
D0EbtFpKd71ng+CT516nDOeB0/RSrFOyA8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXte
JajCq+TA81yc477OMUxkHl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMB
AAGjggM7MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5n
dmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIICIwYKKwYBBAG/VQIB
ADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBl
AHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIAYQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIA
YQBsAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQBy
AGEAYwBpAPMAbgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA
aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMAaQBvAG4AYQBt
AGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQAZQAgAEEAdQB0AG8AcgBpAGQA
YQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBu
AHQAcgBhACAAZQBuACAAbABhACAAZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAA
OgAvAC8AdwB3AHcALgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0
dHA6Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+yeAT8MIGV
BgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQswCQYDVQQGEwJFUzEfMB0G
A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5S
b290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRh
TvW1yEICKrNcda3FbcrnlD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdz
Ckj+IHLtb8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg9J63
NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XFducTZnV+ZfsBn5OH
iJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmCIoaZM3Fa6hlXPZHNqcCjbgcTpsnt
+GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM=
-----END CERTIFICATE-----

A-Trust-nQual-03
================
-----BEGIN CERTIFICATE-----
MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJBVDFIMEYGA1UE
Cgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBpbSBlbGVrdHIuIERhdGVudmVy
a2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5RdWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5R
dWFsLTAzMB4XDTA1MDgxNzIyMDAwMFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgw
RgYDVQQKDD9BLVRydXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0
ZW52ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMMEEEtVHJ1
c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtPWFuA/OQO8BBC4SA
zewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUjlUC5B3ilJfYKvUWG6Nm9wASOhURh73+n
yfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZznF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPE
SU7l0+m0iKsMrmKS1GWH2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4
iHQF63n1k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs2e3V
cuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0OBAoECERqlWdV
eRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAVdRU0VlIXLOThaq/Yy/kgM40
ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fGKOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmr
sQd7TZjTXLDR8KdCoLXEjq/+8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZd
JXDRZslo+S4RFGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS
mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmEDNuxUCAKGkq6
ahq97BvIxYSazQ==
-----END CERTIFICATE-----

TWCA Root Certification Authority
=================================
-----BEGIN CERTIFICATE-----
MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ
VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG
EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB
IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx
QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC
oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP
4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r
y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB
BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG
9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC
mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW
QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY
T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny
Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw==
-----END CERTIFICATE-----

Security Communication RootCA2
==============================
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh
dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC
SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy
aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++
+T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R
3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV
spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K
EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8
QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB
CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj
u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk
3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q
tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29
mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
-----END CERTIFICATE-----

EC-ACC
======
-----BEGIN CERTIFICATE-----
MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE
BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w
ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD
VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE
CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT
BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7
MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt
SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl
Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh
cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK
w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT
ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4
HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a
E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw
0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E
BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD
VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0
Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l
dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ
lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa
Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe
l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2
E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D
5EI=
-----END CERTIFICATE-----

Hellenic Academic and Research Institutions RootCA 2011
=======================================================
-----BEGIN CERTIFICATE-----
MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT
O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y
aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z
IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT
AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z
IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo
IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI
1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa
71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u
8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH
3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/
MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8
MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu
b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt
XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8
TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD
/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N
7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4
-----END CERTIFICATE-----

Actalis Authentication Root CA
==============================
-----BEGIN CERTIFICATE-----
MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM
BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE
AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky
MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz
IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290
IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ
wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa
by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6
zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f
YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2
oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l
EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7
hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8
EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5
jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY
iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt
ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI
WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0
JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx
K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+
Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC
4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo
2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz
lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem
OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9
vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg==
-----END CERTIFICATE-----

Trustis FPS Root CA
===================
-----BEGIN CERTIFICATE-----
MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG
EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290
IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV
BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ
RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk
H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa
cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt
o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA
AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd
BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c
GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC
yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P
8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV
l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl
iB6XzCGcKQENZetX2fNXlrtIzYE=
-----END CERTIFICATE-----

StartCom Certification Authority
================================
-----BEGIN CERTIFICATE-----
MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN
U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu
ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0
NjM3WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk
LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg
U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y
o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/
Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d
eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt
2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z
6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ
osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/
untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc
UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT
37uMdBNSSwIDAQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFulF2mHMMo0aEPQ
Qa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCCATgwLgYIKwYBBQUHAgEWImh0
dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cu
c3RhcnRzc2wuY29tL2ludGVybWVkaWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENv
bW1lcmNpYWwgKFN0YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0
aGUgc2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0aWZpY2F0
aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93d3cuc3RhcnRzc2wuY29t
L3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBG
cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5
fPGFf59Jb2vKXfuM/gTFwWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWm
N3PH/UvSTa0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst0OcN
Org+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNcpRJvkrKTlMeIFw6T
tn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKlCcWw0bdT82AUuoVpaiF8H3VhFyAX
e2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVFP0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA
2MFrLH9ZXF2RsXAiV+uKa0hK1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBs
HvUwyKMQ5bLmKhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE
JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ8dCAWZvLMdib
D4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnmfyWl8kgAwKQB2j8=
-----END CERTIFICATE-----

StartCom Certification Authority G2
===================================
-----BEGIN CERTIFICATE-----
MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMN
U3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
RzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UE
ChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8O
o1XJJZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsDvfOpL9HG
4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnooD/Uefyf3lLE3PbfHkffi
Aez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/Q0kGi4xDuFby2X8hQxfqp0iVAXV16iul
Q5XqFYSdCI0mblWbq9zSOdIxHWDirMxWRST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbs
O+wmETRIjfaAKxojAuuKHDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8H
vKTlXcxNnw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM0D4L
nMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/iUUjXuG+v+E5+M5iS
FGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9Ha90OrInwMEePnWjFqmveiJdnxMa
z6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHgTuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8E
BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJ
KoZIhvcNAQELBQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K
2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfXUfEpY9Z1zRbk
J4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl6/2o1PXWT6RbdejF0mCy2wl+
JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG
/+gyRr61M3Z3qAFdlsHB1b6uJcDJHgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTc
nIhT76IxW1hPkWLIwpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/Xld
blhYXzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5lIxKVCCIc
l85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoohdVddLHRDiBYmxOlsGOm
7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulrso8uBtjRkcfGEvRM/TAXw8HaOFvjqerm
obp573PYtlNXLfbQ4ddI
-----END CERTIFICATE-----

Buypass Class 2 Root CA
=======================
-----BEGIN CERTIFICATE-----
MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X
DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1
eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw
DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1
g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn
9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b
/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU
CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff
awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI
zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn
Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX
Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs
M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD
VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF
AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s
A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI
osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S
aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd
DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD
LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0
oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC
wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS
CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN
rJgWVqA=
-----END CERTIFICATE-----

Buypass Class 3 Root CA
=======================
-----BEGIN CERTIFICATE-----
MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X
DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1
eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw
DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH
sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR
5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh
7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ
ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH
2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV
/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ
RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA
Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq
j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD
VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF
AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV
cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G
uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG
Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8
ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2
KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz
6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug
UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe
eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi
Cp/HuZc=
-----END CERTIFICATE-----

T-TeleSec GlobalRoot Class 3
============================
-----BEGIN CERTIFICATE-----
MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM
IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU
cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx
MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz
dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD
ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK
9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU
NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF
iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W
0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr
AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb
fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT
ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h
P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml
e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw==
-----END CERTIFICATE-----

EE Certification Centre Root CA
===============================
-----BEGIN CERTIFICATE-----
MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG
EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy
dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw
MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB
UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy
ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM
TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2
rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw
93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN
P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T
AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ
MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF
BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj
xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM
lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u
uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU
3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM
dcGWxZ0=
-----END CERTIFICATE-----

TURKTRUST Certificate Services Provider Root 2007
=================================================
-----BEGIN CERTIFICATE-----
MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOcUktUUlVTVCBF
bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP
MA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg
QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4X
DTA3MTIyNTE4MzcxOVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxl
a3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMCVFIxDzAN
BgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp
bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4gKGMpIEFyYWzEsWsgMjAwNzCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9N
YvDdE3ePYakqtdTyuTFYKTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQv
KUmi8wUG+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveGHtya
KhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6PIzdezKKqdfcYbwnT
rqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M733WB2+Y8a+xwXrXgTW4qhe04MsC
AwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHkYb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAP
BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/s
Px+EnWVUXKgWAkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I
aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5mxRZNTZPz/OO
Xl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsaXRik7r4EW5nVcV9VZWRi1aKb
BFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZqxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAK
poRq0Tl9
-----END CERTIFICATE-----

D-TRUST Root Class 3 CA 2 2009
==============================
-----BEGIN CERTIFICATE-----
MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK
DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe
Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE
LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD
ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA
BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv
KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z
p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC
AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ
4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y
eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw
MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G
PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw
OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm
2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0
o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV
dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph
X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I=
-----END CERTIFICATE-----

D-TRUST Root Class 3 CA 2 EV 2009
=================================
-----BEGIN CERTIFICATE-----
MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK
DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw
OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK
DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw
OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS
egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh
zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T
7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60
sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35
11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv
cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v
ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El
MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp
b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh
c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+
PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05
nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX
ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA
NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv
w9y4AyHqnxbxLFS1
-----END CERTIFICATE-----

PSCProcert
==========
-----BEGIN CERTIFICATE-----
MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1dG9yaWRhZCBk
ZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9sYW5vMQswCQYDVQQGEwJWRTEQ
MA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlzdHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lz
dGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBl
cmludGVuZGVuY2lhIGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUw
IwYJKoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEwMFoXDTIw
MTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHByb2NlcnQubmV0LnZlMQ8w
DQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGExKjAoBgNVBAsTIVByb3ZlZWRvciBkZSBD
ZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZp
Y2FjaW9uIEVsZWN0cm9uaWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIw
DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo97BVC
wfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74BCXfgI8Qhd19L3uA
3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38GieU89RLAu9MLmV+QfI4tL3czkkoh
RqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmO
EO8GqQKJ/+MMbpfg353bIdD0PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG2
0qCZyFSTXai20b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH
0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/6mnbVSKVUyqU
td+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1mv6JpIzi4mWCZDlZTOpx+FIyw
Bm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvp
r2uKGcfLFFb14dq12fy/czja+eevbqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/
AgEBMDcGA1UdEgQwMC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAz
Ni0wMB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFDgBStuyId
xuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0b3JpZGFkIGRlIENlcnRp
ZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xhbm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQH
EwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5h
Y2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5k
ZW5jaWEgZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkqhkiG
9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQDAgEGME0GA1UdEQRG
MESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0wMDAwMDKgGwYFYIZeAgKgEgwQUklG
LUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEagRKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52
ZS9sY3IvQ0VSVElGSUNBRE8tUkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNy
YWl6LnN1c2NlcnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v
Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsGAQUFBwIBFh5o
dHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcNAQELBQADggIBACtZ6yKZu4Sq
T96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmN
g7+mvTV+LFwxNG9s2/NkAZiqlCxB3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4q
uxtxj7mkoP3YldmvWb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1
n8GhHVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHmpHmJWhSn
FFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXzsOfIt+FTvZLm8wyWuevo
5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bEqCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq
3TNWOByyrYDT13K9mmyZY+gAu0F2BbdbmRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5
poLWccret9W6aAjtmcz9opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3Y
eMLEYC/HYvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km
-----END CERTIFICATE-----

China Internet Network Information Center EV Certificates Root
==============================================================
-----BEGIN CERTIFICATE-----
MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCQ04xMjAwBgNV
BAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyMUcwRQYDVQQDDD5D
aGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMg
Um9vdDAeFw0xMDA4MzEwNzExMjVaFw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAG
A1UECgwpQ2hpbmEgSW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMM
PkNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRpZmljYXRl
cyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z7r07eKpkQ0H1UN+U8i6y
jUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA//DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV
98YPjUesWgbdYavi7NifFy2cyjw1l1VxzUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2H
klY0bBoQCxfVWhyXWIQ8hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23
KzhmBsUs4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54ugQEC
7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oYNJKiyoOCWTAPBgNV
HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfHJLOcfA22KlT5uqGDSSosqD
glkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd5
0XPFtQO3WKwMVC/GVhMPMdoG52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM
7+czV0I664zBechNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws
ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrIzo9uoV1/A3U0
5K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATywy39FCqQmbkHzJ8=
-----END CERTIFICATE-----

Swisscom Root CA 2
==================
-----BEGIN CERTIFICATE-----
MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQG
EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy
dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2
MjUwNzM4MTRaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln
aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIIC
IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvErjw0DzpPM
LgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r0rk0X2s682Q2zsKwzxNo
ysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJ
wDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVPACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpH
Wrumnf2U5NGKpV+GY3aFy6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1a
SgJA/MTAtukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL6yxS
NLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0uPoTXGiTOmekl9Ab
mbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrALacywlKinh/LTSlDcX3KwFnUey7QY
Ypqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velhk6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3
qPyZ7iVNTA6z00yPhOgpD/0QVAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw
HQYDVR0hBBYwFDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O
BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqhb97iEoHF8Twu
MA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4RfbgZPnm3qKhyN2abGu2sEzsO
v2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ
82YqZh6NM4OKb3xuqFp1mrjX2lhIREeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLz
o9v/tdhZsnPdTSpxsrpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcs
a0vvaGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciATwoCqISxx
OQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99nBjx8Oto0QuFmtEYE3saW
mA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5Wt6NlUe07qxS/TFED6F+KBZvuim6c779o
+sjaC+NCydAXFJy3SuCvkychVSa1ZC+N8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TC
rvJcwhbtkj6EPnNgiLx29CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX
5OfNeOI5wSsSnqaeG8XmDtkx2Q==
-----END CERTIFICATE-----

Swisscom Root EV CA 2
=====================
-----BEGIN CERTIFICATE-----
MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAwZzELMAkGA1UE
BhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdpdGFsIENlcnRpZmljYXRlIFNl
cnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcN
MzEwNjI1MDg0NTA4WjBnMQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsT
HERpZ2l0YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYg
Q0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7BxUglgRCgz
o3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD1ycfMQ4jFrclyxy0uYAy
Xhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPHoCE2G3pXKSinLr9xJZDzRINpUKTk4Rti
GZQJo/PDvO/0vezbE53PnUgJUmfANykRHvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8Li
qG12W0OfvrSdsyaGOx9/5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaH
Za0zKcQvidm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHLOdAG
alNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaCNYGu+HuB5ur+rPQa
m3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f46Fq9mDU5zXNysRojddxyNMkM3Ox
bPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCBUWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDi
xzgHcgplwLa7JSnaFp6LNYth7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/
BAQDAgGGMB0GA1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED
MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWBbj2ITY1x0kbB
bkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6xXCX5145v9Ydkn+0UjrgEjihL
j6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98TPLr+flaYC/NUn81ETm484T4VvwYmneTwkLbU
wp4wLh/vx3rEUMfqe9pQy3omywC0Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7
XwgiG/W9mR4U9s70WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH
59yLGn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm7JFe3VE/
23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4Snr8PyQUQ3nqjsTzyP6Wq
J3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VNvBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyA
HmBR3NdUIR7KYndP+tiPsys6DXhyyWhBWkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/gi
uMod89a2GQ+fYWVq6nTIfI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuW
l8PVP3wbI+2ksx0WckNLIOFZfsLorSa/ovc=
-----END CERTIFICATE-----

CA Disig Root R1
================
-----BEGIN CERTIFICATE-----
MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw
EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp
ZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx
EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp
c2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy
3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8
u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2
m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk
CiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa
YVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6
vpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL
LhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX
ZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is
XxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV
HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ
04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR
xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B
LxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM
CrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb
VSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85
YmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS
ds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix
lAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N
UaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ
a7+h89n07eLw4+1knj0vllJPgFOL
-----END CERTIFICATE-----

CA Disig Root R2
================
-----BEGIN CERTIFICATE-----
MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw
EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp
ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx
EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp
c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC
w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia
xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7
A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S
GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV
g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa
5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE
koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A
Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i
Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV
HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u
Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM
tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV
sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je
dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8
1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx
mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01
utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0
sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg
UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV
7+ZtsH8tZ/3zbBt1RqPlShfppNcL
-----END CERTIFICATE-----

ACCVRAIZ1
=========
-----BEGIN CERTIFICATE-----
MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB
SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1
MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH
UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM
jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0
RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD
aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ
0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG
WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7
8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR
5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J
9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK
Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw
Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu
Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2
VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM
Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA
QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh
AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA
YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj
AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA
IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk
aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0
dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2
MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI
hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E
R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN
YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49
nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ
TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3
sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h
I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg
Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd
3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p
EfbRD0tVNEYqi4Y7
-----END CERTIFICATE-----

TWCA Global Root CA
===================
-----BEGIN CERTIFICATE-----
MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT
CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD
QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK
EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg
Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C
nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV
r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR
Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV
tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W
KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99
sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p
yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn
kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI
zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC
AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g
cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn
LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M
8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg
/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg
lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP
A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m
i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8
EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3
zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0=
-----END CERTIFICATE-----

TeliaSonera Root CA v1
======================
-----BEGIN CERTIFICATE-----
MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE
CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4
MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW
VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+
6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA
3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k
B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn
Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH
oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3
F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ
oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7
gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc
TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB
AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW
DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm
zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx
0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW
pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV
G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc
c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT
JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2
qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6
Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems
WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
-----END CERTIFICATE-----

E-Tugra Certification Authority
===============================
-----BEGIN CERTIFICATE-----
MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w
DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls
ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN
ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw
NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx
QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl
cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD
DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd
hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K
CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g
ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ
BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0
E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz
rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq
jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn
rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5
dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB
/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG
MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK
kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO
XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807
VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo
a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc
dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV
KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT
Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0
8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G
C7TbO6Orb1wdtn7os4I07QZcJA==
-----END CERTIFICATE-----

T-TeleSec GlobalRoot Class 2
============================
-----BEGIN CERTIFICATE-----
MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM
IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU
cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx
MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz
dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD
ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ
SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F
vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970
2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV
WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy
YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4
r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf
vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR
3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN
9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg==
-----END CERTIFICATE-----

Atos TrustedRoot 2011
=====================
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU
cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4
MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG
A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV
hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr
54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+
DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320
HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR
z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R
l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ
bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h
k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh
TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9
61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G
3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed
-----END CERTIFICATE-----

<?php

namespace Guzzle\Http;

use Guzzle\Http\Client;
use Guzzle\Http\ClientInterface;
use Guzzle\Stream\StreamRequestFactoryInterface;
use Guzzle\Stream\PhpStreamRequestFactory;

/**
 * Simplified interface to Guzzle that does not require a class to be instantiated
 */
final class StaticClient
{
    /** @var Client Guzzle client */
    private static $client;

    /**
     * Mount the client to a simpler class name for a specific client
     *
     * @param string          $className Class name to use to mount
     * @param ClientInterface $client    Client used to send requests
     */
    public static function mount($className = 'Guzzle', ClientInterface $client = null)
    {
        class_alias(__CLASS__, $className);
        if ($client) {
            self::$client = $client;
        }
    }

    /**
     * @param  string $method  HTTP request method (GET, POST, HEAD, DELETE, PUT, etc)
     * @param  string $url     URL of the request
     * @param  array  $options Options to use with the request. See: Guzzle\Http\Message\RequestFactory::applyOptions()
     * @return \Guzzle\Http\Message\Response|\Guzzle\Stream\Stream
     */
    public static function request($method, $url, $options = array())
    {
        // @codeCoverageIgnoreStart
        if (!self::$client) {
            self::$client = new Client();
        }
        // @codeCoverageIgnoreEnd

        $request = self::$client->createRequest($method, $url, null, null, $options);

        if (isset($options['stream'])) {
            if ($options['stream'] instanceof StreamRequestFactoryInterface) {
                return $options['stream']->fromRequest($request);
            } elseif ($options['stream'] == true) {
                $streamFactory = new PhpStreamRequestFactory();
                return $streamFactory->fromRequest($request);
            }
        }

        return $request->send();
    }

    /**
     * Send a GET request
     *
     * @param string $url     URL of the request
     * @param array  $options Array of request options
     *
     * @return \Guzzle\Http\Message\Response
     * @see Guzzle::request for a list of available options
     */
    public static function get($url, $options = array())
    {
        return self::request('GET', $url, $options);
    }

    /**
     * Send a HEAD request
     *
     * @param string $url     URL of the request
     * @param array  $options Array of request options
     *
     * @return \Guzzle\Http\Message\Response
     * @see Guzzle::request for a list of available options
     */
    public static function head($url, $options = array())
    {
        return self::request('HEAD', $url, $options);
    }

    /**
     * Send a DELETE request
     *
     * @param string $url     URL of the request
     * @param array  $options Array of request options
     *
     * @return \Guzzle\Http\Message\Response
     * @see Guzzle::request for a list of available options
     */
    public static function delete($url, $options = array())
    {
        return self::request('DELETE', $url, $options);
    }

    /**
     * Send a POST request
     *
     * @param string $url     URL of the request
     * @param array  $options Array of request options
     *
     * @return \Guzzle\Http\Message\Response
     * @see Guzzle::request for a list of available options
     */
    public static function post($url, $options = array())
    {
        return self::request('POST', $url, $options);
    }

    /**
     * Send a PUT request
     *
     * @param string $url     URL of the request
     * @param array  $options Array of request options
     *
     * @return \Guzzle\Http\Message\Response
     * @see Guzzle::request for a list of available options
     */
    public static function put($url, $options = array())
    {
        return self::request('PUT', $url, $options);
    }

    /**
     * Send a PATCH request
     *
     * @param string $url     URL of the request
     * @param array  $options Array of request options
     *
     * @return \Guzzle\Http\Message\Response
     * @see Guzzle::request for a list of available options
     */
    public static function patch($url, $options = array())
    {
        return self::request('PATCH', $url, $options);
    }

    /**
     * Send an OPTIONS request
     *
     * @param string $url     URL of the request
     * @param array  $options Array of request options
     *
     * @return \Guzzle\Http\Message\Response
     * @see Guzzle::request for a list of available options
     */
    public static function options($url, $options = array())
    {
        return self::request('OPTIONS', $url, $options);
    }
}
<?php

namespace Guzzle\Http;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * Parses and generates URLs based on URL parts. In favor of performance, URL parts are not validated.
 */
class Url
{
    protected $scheme;
    protected $host;
    protected $port;
    protected $username;
    protected $password;
    protected $path = '';
    protected $fragment;

    /** @var QueryString Query part of the URL */
    protected $query;

    /**
     * Factory method to create a new URL from a URL string
     *
     * @param string $url Full URL used to create a Url object
     *
     * @return Url
     * @throws InvalidArgumentException
     */
    public static function factory($url)
    {
        static $defaults = array('scheme' => null, 'host' => null, 'path' => null, 'port' => null, 'query' => null,
            'user' => null, 'pass' => null, 'fragment' => null);

        if (false === ($parts = parse_url($url))) {
            throw new InvalidArgumentException('Was unable to parse malformed url: ' . $url);
        }

        $parts += $defaults;

        // Convert the query string into a QueryString object
        if ($parts['query'] || 0 !== strlen($parts['query'])) {
            $parts['query'] = QueryString::fromString($parts['query']);
        }

        return new static($parts['scheme'], $parts['host'], $parts['user'],
            $parts['pass'], $parts['port'], $parts['path'], $parts['query'],
            $parts['fragment']);
    }

    /**
     * Build a URL from parse_url parts. The generated URL will be a relative URL if a scheme or host are not provided.
     *
     * @param array $parts Array of parse_url parts
     *
     * @return string
     */
    public static function buildUrl(array $parts)
    {
        $url = $scheme = '';

        if (isset($parts['scheme'])) {
            $scheme = $parts['scheme'];
            $url .= $scheme . ':';
        }

        if (isset($parts['host'])) {
            $url .= '//';
            if (isset($parts['user'])) {
                $url .= $parts['user'];
                if (isset($parts['pass'])) {
                    $url .= ':' . $parts['pass'];
                }
                $url .=  '@';
            }

            $url .= $parts['host'];

            // Only include the port if it is not the default port of the scheme
            if (isset($parts['port'])
                && !(($scheme == 'http' && $parts['port'] == 80) || ($scheme == 'https' && $parts['port'] == 443))
            ) {
                $url .= ':' . $parts['port'];
            }
        }

        // Add the path component if present
        if (isset($parts['path']) && 0 !== strlen($parts['path'])) {
            // Always ensure that the path begins with '/' if set and something is before the path
            if ($url && $parts['path'][0] != '/' && substr($url, -1)  != '/') {
                $url .= '/';
            }
            $url .= $parts['path'];
        }

        // Add the query string if present
        if (isset($parts['query'])) {
            $url .= '?' . $parts['query'];
        }

        // Ensure that # is only added to the url if fragment contains anything.
        if (isset($parts['fragment'])) {
            $url .= '#' . $parts['fragment'];
        }

        return $url;
    }

    /**
     * Create a new URL from URL parts
     *
     * @param string                   $scheme   Scheme of the URL
     * @param string                   $host     Host of the URL
     * @param string                   $username Username of the URL
     * @param string                   $password Password of the URL
     * @param int                      $port     Port of the URL
     * @param string                   $path     Path of the URL
     * @param QueryString|array|string $query    Query string of the URL
     * @param string                   $fragment Fragment of the URL
     */
    public function __construct($scheme, $host, $username = null, $password = null, $port = null, $path = null, QueryString $query = null, $fragment = null)
    {
        $this->scheme = $scheme;
        $this->host = $host;
        $this->port = $port;
        $this->username = $username;
        $this->password = $password;
        $this->fragment = $fragment;
        if (!$query) {
            $this->query = new QueryString();
        } else {
            $this->setQuery($query);
        }
        $this->setPath($path);
    }

    /**
     * Clone the URL
     */
    public function __clone()
    {
        $this->query = clone $this->query;
    }

    /**
     * Returns the URL as a URL string
     *
     * @return string
     */
    public function __toString()
    {
        return self::buildUrl($this->getParts());
    }

    /**
     * Get the parts of the URL as an array
     *
     * @return array
     */
    public function getParts()
    {
        $query = (string) $this->query;

        return array(
            'scheme' => $this->scheme,
            'user' => $this->username,
            'pass' => $this->password,
            'host' => $this->host,
            'port' => $this->port,
            'path' => $this->getPath(),
            'query' => $query !== '' ? $query : null,
            'fragment' => $this->fragment,
        );
    }

    /**
     * Set the host of the request.
     *
     * @param string $host Host to set (e.g. www.yahoo.com, yahoo.com)
     *
     * @return Url
     */
    public function setHost($host)
    {
        if (strpos($host, ':') === false) {
            $this->host = $host;
        } else {
            list($host, $port) = explode(':', $host);
            $this->host = $host;
            $this->setPort($port);
        }

        return $this;
    }

    /**
     * Get the host part of the URL
     *
     * @return string
     */
    public function getHost()
    {
        return $this->host;
    }

    /**
     * Set the scheme part of the URL (http, https, ftp, etc)
     *
     * @param string $scheme Scheme to set
     *
     * @return Url
     */
    public function setScheme($scheme)
    {
        if ($this->scheme == 'http' && $this->port == 80) {
            $this->port = null;
        } elseif ($this->scheme == 'https' && $this->port == 443) {
            $this->port = null;
        }

        $this->scheme = $scheme;

        return $this;
    }

    /**
     * Get the scheme part of the URL
     *
     * @return string
     */
    public function getScheme()
    {
        return $this->scheme;
    }

    /**
     * Set the port part of the URL
     *
     * @param int $port Port to set
     *
     * @return Url
     */
    public function setPort($port)
    {
        $this->port = $port;

        return $this;
    }

    /**
     * Get the port part of the URl. Will return the default port for a given scheme if no port has been set.
     *
     * @return int|null
     */
    public function getPort()
    {
        if ($this->port) {
            return $this->port;
        } elseif ($this->scheme == 'http') {
            return 80;
        } elseif ($this->scheme == 'https') {
            return 443;
        }

        return null;
    }

    /**
     * Set the path part of the URL
     *
     * @param array|string $path Path string or array of path segments
     *
     * @return Url
     */
    public function setPath($path)
    {
        static $pathReplace = array(' ' => '%20', '?' => '%3F');
        if (is_array($path)) {
            $path = '/' . implode('/', $path);
        }

        $this->path = strtr($path, $pathReplace);

        return $this;
    }

    /**
     * Normalize the URL so that double slashes and relative paths are removed
     *
     * @return Url
     */
    public function normalizePath()
    {
        if (!$this->path || $this->path == '/' || $this->path == '*') {
            return $this;
        }

        $results = array();
        $segments = $this->getPathSegments();
        foreach ($segments as $segment) {
            if ($segment == '..') {
                array_pop($results);
            } elseif ($segment != '.' && $segment != '') {
                $results[] = $segment;
            }
        }

        // Combine the normalized parts and add the leading slash if needed
        $this->path = ($this->path[0] == '/' ? '/' : '') . implode('/', $results);

        // Add the trailing slash if necessary
        if ($this->path != '/' && end($segments) == '') {
            $this->path .= '/';
        }

        return $this;
    }

    /**
     * Add a relative path to the currently set path.
     *
     * @param string $relativePath Relative path to add
     *
     * @return Url
     */
    public function addPath($relativePath)
    {
        if ($relativePath != '/' && is_string($relativePath) && strlen($relativePath) > 0) {
            // Add a leading slash if needed
            if ($relativePath[0] != '/') {
                $relativePath = '/' . $relativePath;
            }
            $this->setPath(str_replace('//', '/', $this->path . $relativePath));
        }

        return $this;
    }

    /**
     * Get the path part of the URL
     *
     * @return string
     */
    public function getPath()
    {
        return $this->path;
    }

    /**
     * Get the path segments of the URL as an array
     *
     * @return array
     */
    public function getPathSegments()
    {
        return array_slice(explode('/', $this->getPath()), 1);
    }

    /**
     * Set the password part of the URL
     *
     * @param string $password Password to set
     *
     * @return Url
     */
    public function setPassword($password)
    {
        $this->password = $password;

        return $this;
    }

    /**
     * Get the password part of the URL
     *
     * @return null|string
     */
    public function getPassword()
    {
        return $this->password;
    }

    /**
     * Set the username part of the URL
     *
     * @param string $username Username to set
     *
     * @return Url
     */
    public function setUsername($username)
    {
        $this->username = $username;

        return $this;
    }

    /**
     * Get the username part of the URl
     *
     * @return null|string
     */
    public function getUsername()
    {
        return $this->username;
    }

    /**
     * Get the query part of the URL as a QueryString object
     *
     * @return QueryString
     */
    public function getQuery()
    {
        return $this->query;
    }

    /**
     * Set the query part of the URL
     *
     * @param QueryString|string|array $query Query to set
     *
     * @return Url
     */
    public function setQuery($query)
    {
        if (is_string($query)) {
            $output = null;
            parse_str($query, $output);
            $this->query = new QueryString($output);
        } elseif (is_array($query)) {
            $this->query = new QueryString($query);
        } elseif ($query instanceof QueryString) {
            $this->query = $query;
        }

        return $this;
    }

    /**
     * Get the fragment part of the URL
     *
     * @return null|string
     */
    public function getFragment()
    {
        return $this->fragment;
    }

    /**
     * Set the fragment part of the URL
     *
     * @param string $fragment Fragment to set
     *
     * @return Url
     */
    public function setFragment($fragment)
    {
        $this->fragment = $fragment;

        return $this;
    }

    /**
     * Check if this is an absolute URL
     *
     * @return bool
     */
    public function isAbsolute()
    {
        return $this->scheme && $this->host;
    }

    /**
     * Combine the URL with another URL. Follows the rules specific in RFC 3986 section 5.4.
     *
     * @param string $url           Relative URL to combine with
     * @param bool   $strictRfc3986 Set to true to use strict RFC 3986 compliance when merging paths. When first
     *                              released, Guzzle used an incorrect algorithm for combining relative URL paths. In
     *                              order to not break users, we introduced this flag to allow the merging of URLs based
     *                              on strict RFC 3986 section 5.4.1. This means that "http://a.com/foo/baz" merged with
     *                              "bar" would become "http://a.com/foo/bar". When this value is set to false, it would
     *                              become "http://a.com/foo/baz/bar".
     * @return Url
     * @throws InvalidArgumentException
     * @link http://tools.ietf.org/html/rfc3986#section-5.4
     */
    public function combine($url, $strictRfc3986 = false)
    {
        $url = self::factory($url);

        // Use the more absolute URL as the base URL
        if (!$this->isAbsolute() && $url->isAbsolute()) {
            $url = $url->combine($this);
        }

        // Passing a URL with a scheme overrides everything
        if ($buffer = $url->getScheme()) {
            $this->scheme = $buffer;
            $this->host = $url->getHost();
            $this->port = $url->getPort();
            $this->username = $url->getUsername();
            $this->password = $url->getPassword();
            $this->path = $url->getPath();
            $this->query = $url->getQuery();
            $this->fragment = $url->getFragment();
            return $this;
        }

        // Setting a host overrides the entire rest of the URL
        if ($buffer = $url->getHost()) {
            $this->host = $buffer;
            $this->port = $url->getPort();
            $this->username = $url->getUsername();
            $this->password = $url->getPassword();
            $this->path = $url->getPath();
            $this->query = $url->getQuery();
            $this->fragment = $url->getFragment();
            return $this;
        }

        $path = $url->getPath();
        $query = $url->getQuery();

        if (!$path) {
            if (count($query)) {
                $this->addQuery($query, $strictRfc3986);
            }
        } else {
            if ($path[0] == '/') {
                $this->path = $path;
            } elseif ($strictRfc3986) {
                $this->path .= '/../' . $path;
            } else {
                $this->path .= '/' . $path;
            }
            $this->normalizePath();
            $this->addQuery($query, $strictRfc3986);
        }

        $this->fragment = $url->getFragment();

        return $this;
    }

    private function addQuery(QueryString $new, $strictRfc386)
    {
        if (!$strictRfc386) {
            $new->merge($this->query);
        }

        $this->query = $new;
    }
}
<?php

namespace Guzzle\Inflection;

/**
 * Default inflection implementation
 */
class Inflector implements InflectorInterface
{
    /** @var InflectorInterface */
    protected static $default;

    /**
     * Get the default inflector object that has support for caching
     *
     * @return MemoizingInflector
     */
    public static function getDefault()
    {
        // @codeCoverageIgnoreStart
        if (!self::$default) {
            self::$default = new MemoizingInflector(new self());
        }
        // @codeCoverageIgnoreEnd

        return self::$default;
    }

    public function snake($word)
    {
        return ctype_lower($word) ? $word : strtolower(preg_replace('/(.)([A-Z])/', "$1_$2", $word));
    }

    public function camel($word)
    {
        return str_replace(' ', '', ucwords(strtr($word, '_-', '  ')));
    }
}
<?php

namespace Guzzle\Inflection;

/**
 * Inflector interface used to convert the casing of words
 */
interface InflectorInterface
{
    /**
     * Converts strings from camel case to snake case (e.g. CamelCase camel_case).
     *
     * @param string $word Word to convert to snake case
     *
     * @return string
     */
    public function snake($word);

    /**
     * Converts strings from snake_case to upper CamelCase
     *
     * @param string $word Value to convert into upper CamelCase
     *
     * @return string
     */
    public function camel($word);
}
<?php

namespace Guzzle\Inflection;

/**
 * Decorator used to add memoization to previously inflected words
 */
class MemoizingInflector implements InflectorInterface
{
    /** @var array Array of cached inflections */
    protected $cache = array(
        'snake' => array(),
        'camel' => array()
    );

    /** @var int Max entries per cache */
    protected $maxCacheSize;

    /** @var InflectorInterface Decorated inflector */
    protected $decoratedInflector;

    /**
     * @param InflectorInterface $inflector    Inflector being decorated
     * @param int                $maxCacheSize Maximum number of cached items to hold per cache
     */
    public function __construct(InflectorInterface $inflector, $maxCacheSize = 500)
    {
        $this->decoratedInflector = $inflector;
        $this->maxCacheSize = $maxCacheSize;
    }

    public function snake($word)
    {
        if (!isset($this->cache['snake'][$word])) {
            $this->pruneCache('snake');
            $this->cache['snake'][$word] = $this->decoratedInflector->snake($word);
        }

        return $this->cache['snake'][$word];
    }

    /**
     * Converts strings from snake_case to upper CamelCase
     *
     * @param string $word Value to convert into upper CamelCase
     *
     * @return string
     */
    public function camel($word)
    {
        if (!isset($this->cache['camel'][$word])) {
            $this->pruneCache('camel');
            $this->cache['camel'][$word] = $this->decoratedInflector->camel($word);
        }

        return $this->cache['camel'][$word];
    }

    /**
     * Prune one of the named caches by removing 20% of the cache if it is full
     *
     * @param string $cache Type of cache to prune
     */
    protected function pruneCache($cache)
    {
        if (count($this->cache[$cache]) == $this->maxCacheSize) {
            $this->cache[$cache] = array_slice($this->cache[$cache], $this->maxCacheSize * 0.2);
        }
    }
}
<?php

namespace Guzzle\Inflection;

/**
 * Decorator used to add pre-computed inflection mappings to an inflector
 */
class PreComputedInflector implements InflectorInterface
{
    /** @var array Array of pre-computed inflections */
    protected $mapping = array(
        'snake' => array(),
        'camel' => array()
    );

    /** @var InflectorInterface Decorated inflector */
    protected $decoratedInflector;

    /**
     * @param InflectorInterface $inflector Inflector being decorated
     * @param array              $snake     Hash of pre-computed camel to snake
     * @param array              $camel     Hash of pre-computed snake to camel
     * @param bool               $mirror    Mirror snake and camel reflections
     */
    public function __construct(InflectorInterface $inflector, array $snake = array(), array $camel = array(), $mirror = false)
    {
        if ($mirror) {
            $camel = array_merge(array_flip($snake), $camel);
            $snake = array_merge(array_flip($camel), $snake);
        }

        $this->decoratedInflector = $inflector;
        $this->mapping = array(
            'snake' => $snake,
            'camel' => $camel
        );
    }

    public function snake($word)
    {
        return isset($this->mapping['snake'][$word])
            ? $this->mapping['snake'][$word]
            : $this->decoratedInflector->snake($word);
    }

    /**
     * Converts strings from snake_case to upper CamelCase
     *
     * @param string $word Value to convert into upper CamelCase
     *
     * @return string
     */
    public function camel($word)
    {
        return isset($this->mapping['camel'][$word])
            ? $this->mapping['camel'][$word]
            : $this->decoratedInflector->camel($word);
    }
}
<?php

namespace Guzzle\Iterator;

/**
 * AppendIterator that is not affected by https://bugs.php.net/bug.php?id=49104
 */
class AppendIterator extends \AppendIterator
{
    /**
     * Works around the bug in which PHP calls rewind() and next() when appending
     *
     * @param \Iterator $iterator Iterator to append
     */
    public function append(\Iterator $iterator)
    {
        $this->getArrayIterator()->append($iterator);
    }
}
<?php

namespace Guzzle\Iterator;

/**
 * Pulls out chunks from an inner iterator and yields the chunks as arrays
 */
class ChunkedIterator extends \IteratorIterator
{
    /** @var int Size of each chunk */
    protected $chunkSize;

    /** @var array Current chunk */
    protected $chunk;

    /**
     * @param \Traversable $iterator  Traversable iterator
     * @param int          $chunkSize Size to make each chunk
     * @throws \InvalidArgumentException
     */
    public function __construct(\Traversable $iterator, $chunkSize)
    {
        $chunkSize = (int) $chunkSize;
        if ($chunkSize < 0 ) {
            throw new \InvalidArgumentException("The chunk size must be equal or greater than zero; $chunkSize given");
        }

        parent::__construct($iterator);
        $this->chunkSize = $chunkSize;
    }

    public function rewind()
    {
        parent::rewind();
        $this->next();
    }

    public function next()
    {
        $this->chunk = array();
        for ($i = 0; $i < $this->chunkSize && parent::valid(); $i++) {
            $this->chunk[] = parent::current();
            parent::next();
        }
    }

    public function current()
    {
        return $this->chunk;
    }

    public function valid()
    {
        return (bool) $this->chunk;
    }
}
<?php

namespace Guzzle\Iterator;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * Filters values using a callback
 *
 * Used when PHP 5.4's {@see \CallbackFilterIterator} is not available
 */
class FilterIterator extends \FilterIterator
{
    /** @var mixed Callback used for filtering */
    protected $callback;

    /**
     * @param \Iterator      $iterator Traversable iterator
     * @param array|\Closure $callback Callback used for filtering. Return true to keep or false to filter.
     *
     * @throws InvalidArgumentException if the callback if not callable
     */
    public function __construct(\Iterator $iterator, $callback)
    {
        parent::__construct($iterator);
        if (!is_callable($callback)) {
            throw new InvalidArgumentException('The callback must be callable');
        }
        $this->callback = $callback;
    }

    public function accept()
    {
        return call_user_func($this->callback, $this->current());
    }
}
<?php

namespace Guzzle\Iterator;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * Maps values before yielding
 */
class MapIterator extends \IteratorIterator
{
    /** @var mixed Callback */
    protected $callback;

    /**
     * @param \Traversable   $iterator Traversable iterator
     * @param array|\Closure $callback Callback used for iterating
     *
     * @throws InvalidArgumentException if the callback if not callable
     */
    public function __construct(\Traversable $iterator, $callback)
    {
        parent::__construct($iterator);
        if (!is_callable($callback)) {
            throw new InvalidArgumentException('The callback must be callable');
        }
        $this->callback = $callback;
    }

    public function current()
    {
        return call_user_func($this->callback, parent::current());
    }
}
<?php

namespace Guzzle\Iterator;

/**
 * Proxies missing method calls to the innermost iterator
 */
class MethodProxyIterator extends \IteratorIterator
{
    /**
     * Proxy method calls to the wrapped iterator
     *
     * @param string $name Name of the method
     * @param array  $args Arguments to proxy
     *
     * @return mixed
     */
    public function __call($name, array $args)
    {
        $i = $this->getInnerIterator();
        while ($i instanceof \OuterIterator) {
            $i = $i->getInnerIterator();
        }

        return call_user_func_array(array($i, $name), $args);
    }
}
<?php

namespace Guzzle\Log;

/**
 * Adapter class that allows Guzzle to log data using various logging implementations
 */
abstract class AbstractLogAdapter implements LogAdapterInterface
{
    protected $log;

    public function getLogObject()
    {
        return $this->log;
    }
}
<?php

namespace Guzzle\Log;

/**
 * Stores all log messages in an array
 */
class ArrayLogAdapter implements LogAdapterInterface
{
    protected $logs = array();

    public function log($message, $priority = LOG_INFO, $extras = array())
    {
        $this->logs[] = array('message' => $message, 'priority' => $priority, 'extras' => $extras);
    }

    /**
     * Get logged entries
     *
     * @return array
     */
    public function getLogs()
    {
        return $this->logs;
    }

    /**
     * Clears logged entries
     */
    public function clearLogs()
    {
        $this->logs = array();
    }
}
<?php

namespace Guzzle\Log;

/**
 * Logs messages using Closures. Closures combined with filtering can trigger application events based on log messages.
 */
class ClosureLogAdapter extends AbstractLogAdapter
{
    public function __construct($logObject)
    {
        if (!is_callable($logObject)) {
            throw new \InvalidArgumentException('Object must be callable');
        }

        $this->log = $logObject;
    }

    public function log($message, $priority = LOG_INFO, $extras = array())
    {
        call_user_func($this->log, $message, $priority, $extras);
    }
}
<?php

namespace Guzzle\Log;

/**
 * Adapter class that allows Guzzle to log data to various logging implementations.
 */
interface LogAdapterInterface
{
    /**
     * Log a message at a priority
     *
     * @param string  $message  Message to log
     * @param integer $priority Priority of message (use the \LOG_* constants of 0 - 7)
     * @param array   $extras   Extra information to log in event
     */
    public function log($message, $priority = LOG_INFO, $extras = array());
}
<?php

namespace Guzzle\Log;

use Guzzle\Http\Curl\CurlHandle;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Message formatter used in various places in the framework
 *
 * Format messages using a template that can contain the the following variables:
 *
 * - {request}:       Full HTTP request message
 * - {response}:      Full HTTP response message
 * - {ts}:            Timestamp
 * - {host}:          Host of the request
 * - {method}:        Method of the request
 * - {url}:           URL of the request
 * - {host}:          Host of the request
 * - {protocol}:      Request protocol
 * - {version}:       Protocol version
 * - {resource}:      Resource of the request (path + query + fragment)
 * - {port}:          Port of the request
 * - {hostname}:      Hostname of the machine that sent the request
 * - {code}:          Status code of the response (if available)
 * - {phrase}:        Reason phrase of the response  (if available)
 * - {curl_error}:    Curl error message (if available)
 * - {curl_code}:     Curl error code (if available)
 * - {curl_stderr}:   Curl standard error (if available)
 * - {connect_time}:  Time in seconds it took to establish the connection (if available)
 * - {total_time}:    Total transaction time in seconds for last transfer (if available)
 * - {req_header_*}:  Replace `*` with the lowercased name of a request header to add to the message
 * - {res_header_*}:  Replace `*` with the lowercased name of a response header to add to the message
 * - {req_body}:      Request body
 * - {res_body}:      Response body
 */
class MessageFormatter
{
    const DEFAULT_FORMAT = "{hostname} {req_header_User-Agent} - [{ts}] \"{method} {resource} {protocol}/{version}\" {code} {res_header_Content-Length}";
    const DEBUG_FORMAT = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{curl_stderr}";
    const SHORT_FORMAT = '[{ts}] "{method} {resource} {protocol}/{version}" {code}';

    /**
     * @var string Template used to format log messages
     */
    protected $template;

    /**
     * @param string $template Log message template
     */
    public function __construct($template = self::DEFAULT_FORMAT)
    {
        $this->template = $template ?: self::DEFAULT_FORMAT;
    }

    /**
     * Set the template to use for logging
     *
     * @param string $template Log message template
     *
     * @return self
     */
    public function setTemplate($template)
    {
        $this->template = $template;

        return $this;
    }

    /**
     * Returns a formatted message
     *
     * @param RequestInterface $request    Request that was sent
     * @param Response         $response   Response that was received
     * @param CurlHandle       $handle     Curl handle associated with the message
     * @param array            $customData Associative array of custom template data
     *
     * @return string
     */
    public function format(
        RequestInterface $request,
        Response $response = null,
        CurlHandle $handle = null,
        array $customData = array()
    ) {
        $cache = $customData;

        return preg_replace_callback(
            '/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
            function (array $matches) use ($request, $response, $handle, &$cache) {

                if (array_key_exists($matches[1], $cache)) {
                    return $cache[$matches[1]];
                }

                $result = '';
                switch ($matches[1]) {
                    case 'request':
                        $result = (string) $request;
                        break;
                    case 'response':
                        $result = (string) $response;
                        break;
                    case 'req_body':
                        $result = $request instanceof EntityEnclosingRequestInterface
                            ? (string) $request->getBody() : '';
                        break;
                    case 'res_body':
                        $result = $response ? $response->getBody(true) : '';
                        break;
                    case 'ts':
                        $result = gmdate('c');
                        break;
                    case 'method':
                        $result = $request->getMethod();
                        break;
                    case 'url':
                        $result = (string) $request->getUrl();
                        break;
                    case 'resource':
                        $result = $request->getResource();
                        break;
                    case 'protocol':
                        $result = 'HTTP';
                        break;
                    case 'version':
                        $result = $request->getProtocolVersion();
                        break;
                    case 'host':
                        $result = $request->getHost();
                        break;
                    case 'hostname':
                        $result = gethostname();
                        break;
                    case 'port':
                        $result = $request->getPort();
                        break;
                    case 'code':
                        $result = $response ? $response->getStatusCode() : '';
                        break;
                    case 'phrase':
                        $result = $response ? $response->getReasonPhrase() : '';
                        break;
                    case 'connect_time':
                        $result = $handle && $handle->getInfo(CURLINFO_CONNECT_TIME)
                            ? $handle->getInfo(CURLINFO_CONNECT_TIME)
                            : ($response ? $response->getInfo('connect_time') : '');
                        break;
                    case 'total_time':
                        $result = $handle && $handle->getInfo(CURLINFO_TOTAL_TIME)
                            ? $handle->getInfo(CURLINFO_TOTAL_TIME)
                            : ($response ? $response->getInfo('total_time') : '');
                        break;
                    case 'curl_error':
                        $result = $handle ? $handle->getError() : '';
                        break;
                    case 'curl_code':
                        $result = $handle ? $handle->getErrorNo() : '';
                        break;
                    case 'curl_stderr':
                        $result =  $handle ? $handle->getStderr() : '';
                        break;
                    default:
                        if (strpos($matches[1], 'req_header_') === 0) {
                            $result = $request->getHeader(substr($matches[1], 11));
                        } elseif ($response && strpos($matches[1], 'res_header_') === 0) {
                            $result = $response->getHeader(substr($matches[1], 11));
                        }
                }

                $cache[$matches[1]] = $result;
                return $result;
            },
            $this->template
        );
    }
}
<?php

namespace Guzzle\Log;

use Monolog\Logger;

/**
 * @deprecated
 * @codeCoverageIgnore
 */
class MonologLogAdapter extends AbstractLogAdapter
{
    /**
     * syslog to Monolog mappings
     */
    private static $mapping = array(
        LOG_DEBUG   => Logger::DEBUG,
        LOG_INFO    => Logger::INFO,
        LOG_WARNING => Logger::WARNING,
        LOG_ERR     => Logger::ERROR,
        LOG_CRIT    => Logger::CRITICAL,
        LOG_ALERT   => Logger::ALERT
    );

    public function __construct(Logger $logObject)
    {
        $this->log = $logObject;
    }

    public function log($message, $priority = LOG_INFO, $extras = array())
    {
        $this->log->addRecord(self::$mapping[$priority], $message, $extras);
    }
}
<?php

namespace Guzzle\Log;

use Psr\Log\LogLevel;
use Psr\Log\LoggerInterface;

/**
 * PSR-3 log adapter
 *
 * @link https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
 */
class PsrLogAdapter extends AbstractLogAdapter
{
    /**
     * syslog to PSR-3 mappings
     */
    private static $mapping = array(
        LOG_DEBUG   => LogLevel::DEBUG,
        LOG_INFO    => LogLevel::INFO,
        LOG_WARNING => LogLevel::WARNING,
        LOG_ERR     => LogLevel::ERROR,
        LOG_CRIT    => LogLevel::CRITICAL,
        LOG_ALERT   => LogLevel::ALERT
    );

    public function __construct(LoggerInterface $logObject)
    {
        $this->log = $logObject;
    }

    public function log($message, $priority = LOG_INFO, $extras = array())
    {
        $this->log->log(self::$mapping[$priority], $message, $extras);
    }
}
<?php

namespace Guzzle\Log;

use Guzzle\Common\Version;

/**
 * Adapts a Zend Framework 1 logger object
 * @deprecated
 * @codeCoverageIgnore
 */
class Zf1LogAdapter extends AbstractLogAdapter
{
    public function __construct(\Zend_Log $logObject)
    {
        $this->log = $logObject;
        Version::warn(__CLASS__ . ' is deprecated');
    }

    public function log($message, $priority = LOG_INFO, $extras = array())
    {
        $this->log->log($message, $priority, $extras);
    }
}
<?php

namespace Guzzle\Log;

use Zend\Log\Logger;

/**
 * Adapts a Zend Framework 2 logger object
 */
class Zf2LogAdapter extends AbstractLogAdapter
{
    public function __construct(Logger $logObject)
    {
        $this->log = $logObject;
    }

    public function log($message, $priority = LOG_INFO, $extras = array())
    {
        $this->log->log($priority, $message, $extras);
    }
}
<?php

namespace Guzzle\Parser\Cookie;

/**
 * Default Guzzle implementation of a Cookie parser
 */
class CookieParser implements CookieParserInterface
{
    /** @var array Cookie part names to snake_case array values */
    protected static $cookieParts = array(
        'domain'      => 'Domain',
        'path'        => 'Path',
        'max_age'     => 'Max-Age',
        'expires'     => 'Expires',
        'version'     => 'Version',
        'secure'      => 'Secure',
        'port'        => 'Port',
        'discard'     => 'Discard',
        'comment'     => 'Comment',
        'comment_url' => 'Comment-Url',
        'http_only'   => 'HttpOnly'
    );

    public function parseCookie($cookie, $host = null, $path = null, $decode = false)
    {
        // Explode the cookie string using a series of semicolons
        $pieces = array_filter(array_map('trim', explode(';', $cookie)));

        // The name of the cookie (first kvp) must include an equal sign.
        if (empty($pieces) || !strpos($pieces[0], '=')) {
            return false;
        }

        // Create the default return array
        $data = array_merge(array_fill_keys(array_keys(self::$cookieParts), null), array(
            'cookies'   => array(),
            'data'      => array(),
            'path'      => null,
            'http_only' => false,
            'discard'   => false,
            'domain'    => $host
        ));
        $foundNonCookies = 0;

        // Add the cookie pieces into the parsed data array
        foreach ($pieces as $part) {

            $cookieParts = explode('=', $part, 2);
            $key = trim($cookieParts[0]);

            if (count($cookieParts) == 1) {
                // Can be a single value (e.g. secure, httpOnly)
                $value = true;
            } else {
                // Be sure to strip wrapping quotes
                $value = trim($cookieParts[1], " \n\r\t\0\x0B\"");
                if ($decode) {
                    $value = urldecode($value);
                }
            }

            // Only check for non-cookies when cookies have been found
            if (!empty($data['cookies'])) {
                foreach (self::$cookieParts as $mapValue => $search) {
                    if (!strcasecmp($search, $key)) {
                        $data[$mapValue] = $mapValue == 'port' ? array_map('trim', explode(',', $value)) : $value;
                        $foundNonCookies++;
                        continue 2;
                    }
                }
            }

            // If cookies have not yet been retrieved, or this value was not found in the pieces array, treat it as a
            // cookie. IF non-cookies have been parsed, then this isn't a cookie, it's cookie data. Cookies then data.
            $data[$foundNonCookies ? 'data' : 'cookies'][$key] = $value;
        }

        // Calculate the expires date
        if (!$data['expires'] && $data['max_age']) {
            $data['expires'] = time() + (int) $data['max_age'];
        }

        // Check path attribute according RFC6265 http://tools.ietf.org/search/rfc6265#section-5.2.4
        // "If the attribute-value is empty or if the first character of the
        // attribute-value is not %x2F ("/"):
        //   Let cookie-path be the default-path.
        // Otherwise:
        //   Let cookie-path be the attribute-value."
        if (!$data['path'] || substr($data['path'], 0, 1) !== '/') {
            $data['path'] = $this->getDefaultPath($path);
        }

        return $data;
    }

    /**
     * Get default cookie path according to RFC 6265
     * http://tools.ietf.org/search/rfc6265#section-5.1.4 Paths and Path-Match
     *
     * @param string $path Request uri-path
     *
     * @return string
     */
    protected function getDefaultPath($path) {
        // "The user agent MUST use an algorithm equivalent to the following algorithm
        // to compute the default-path of a cookie:"

        // "2. If the uri-path is empty or if the first character of the uri-path is not
        // a %x2F ("/") character, output %x2F ("/") and skip the remaining steps.
        if (empty($path) || substr($path, 0, 1) !== '/') {
            return '/';
        }

        // "3. If the uri-path contains no more than one %x2F ("/") character, output
        // %x2F ("/") and skip the remaining step."
        if ($path === "/") {
            return $path;
        }

        $rightSlashPos = strrpos($path, '/');
        if ($rightSlashPos === 0) {
            return "/";
        }

        // "4. Output the characters of the uri-path from the first character up to,
        // but not including, the right-most %x2F ("/")."
        return substr($path, 0, $rightSlashPos);

    }
}
<?php

namespace Guzzle\Parser\Cookie;

/**
 * Cookie parser interface
 */
interface CookieParserInterface
{
    /**
     * Parse a cookie string as set in a Set-Cookie HTTP header and return an associative array of data.
     *
     * @param string $cookie Cookie header value to parse
     * @param string $host   Host of an associated request
     * @param string $path   Path of an associated request
     * @param bool   $decode Set to TRUE to urldecode cookie values
     *
     * @return array|bool Returns FALSE on failure or returns an array of arrays, with each of the sub arrays including:
     *     - domain  (string) - Domain of the cookie
     *     - path    (string) - Path of the cookie
     *     - cookies (array)  - Associative array of cookie names and values
     *     - max_age (int)    - Lifetime of the cookie in seconds
     *     - version (int)    - Version of the cookie specification. RFC 2965 is 1
     *     - secure  (bool)   - Whether or not this is a secure cookie
     *     - discard (bool)   - Whether or not this is a discardable cookie
     *     - custom (string)  - Custom cookie data array
     *     - comment (string) - How the cookie is intended to be used
     *     - comment_url (str)- URL that contains info on how it will be used
     *     - port (array|str) - Array of ports or null
     *     - http_only (bool) - HTTP only cookie
     */
    public function parseCookie($cookie, $host = null, $path = null, $decode = false);
}
<?php

namespace Guzzle\Parser\Message;

/**
 * Implements shared message parsing functionality
 */
abstract class AbstractMessageParser implements MessageParserInterface
{
    /**
     * Create URL parts from HTTP message parts
     *
     * @param string $requestUrl Associated URL
     * @param array  $parts      HTTP message parts
     *
     * @return array
     */
    protected function getUrlPartsFromMessage($requestUrl, array $parts)
    {
        // Parse the URL information from the message
        $urlParts = array(
            'path'   => $requestUrl,
            'scheme' => 'http'
        );

        // Check for the Host header
        if (isset($parts['headers']['Host'])) {
            $urlParts['host'] = $parts['headers']['Host'];
        } elseif (isset($parts['headers']['host'])) {
            $urlParts['host'] = $parts['headers']['host'];
        } else {
            $urlParts['host'] = null;
        }

        if (false === strpos($urlParts['host'], ':')) {
            $urlParts['port'] = '';
        } else {
            $hostParts = explode(':', $urlParts['host']);
            $urlParts['host'] = trim($hostParts[0]);
            $urlParts['port'] = (int) trim($hostParts[1]);
            if ($urlParts['port'] == 443) {
                $urlParts['scheme'] = 'https';
            }
        }

        // Check if a query is present
        $path = $urlParts['path'];
        $qpos = strpos($path, '?');
        if ($qpos) {
            $urlParts['query'] = substr($path, $qpos + 1);
            $urlParts['path'] = substr($path, 0, $qpos);
        } else {
            $urlParts['query'] = '';
        }

        return $urlParts;
    }
}
<?php

namespace Guzzle\Parser\Message;

/**
 * Default request and response parser used by Guzzle. Optimized for speed.
 */
class MessageParser extends AbstractMessageParser
{
    public function parseRequest($message)
    {
        if (!$message) {
            return false;
        }

        $parts = $this->parseMessage($message);

        // Parse the protocol and protocol version
        if (isset($parts['start_line'][2])) {
            $startParts = explode('/', $parts['start_line'][2]);
            $protocol = strtoupper($startParts[0]);
            $version = isset($startParts[1]) ? $startParts[1] : '1.1';
        } else {
            $protocol = 'HTTP';
            $version = '1.1';
        }

        $parsed = array(
            'method'   => strtoupper($parts['start_line'][0]),
            'protocol' => $protocol,
            'version'  => $version,
            'headers'  => $parts['headers'],
            'body'     => $parts['body']
        );

        $parsed['request_url'] = $this->getUrlPartsFromMessage(isset($parts['start_line'][1]) ? $parts['start_line'][1] : '' , $parsed);

        return $parsed;
    }

    public function parseResponse($message)
    {
        if (!$message) {
            return false;
        }

        $parts = $this->parseMessage($message);
        list($protocol, $version) = explode('/', trim($parts['start_line'][0]));

        return array(
            'protocol'      => $protocol,
            'version'       => $version,
            'code'          => $parts['start_line'][1],
            'reason_phrase' => isset($parts['start_line'][2]) ? $parts['start_line'][2] : '',
            'headers'       => $parts['headers'],
            'body'          => $parts['body']
        );
    }

    /**
     * Parse a message into parts
     *
     * @param string $message Message to parse
     *
     * @return array
     */
    protected function parseMessage($message)
    {
        $startLine = null;
        $headers = array();
        $body = '';

        // Iterate over each line in the message, accounting for line endings
        $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE);
        for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) {

            $line = $lines[$i];

            // If two line breaks were encountered, then this is the end of body
            if (empty($line)) {
                if ($i < $totalLines - 1) {
                    $body = implode('', array_slice($lines, $i + 2));
                }
                break;
            }

            // Parse message headers
            if (!$startLine) {
                $startLine = explode(' ', $line, 3);
            } elseif (strpos($line, ':')) {
                $parts = explode(':', $line, 2);
                $key = trim($parts[0]);
                $value = isset($parts[1]) ? trim($parts[1]) : '';
                if (!isset($headers[$key])) {
                    $headers[$key] = $value;
                } elseif (!is_array($headers[$key])) {
                    $headers[$key] = array($headers[$key], $value);
                } else {
                    $headers[$key][] = $value;
                }
            }
        }

        return array(
            'start_line' => $startLine,
            'headers'    => $headers,
            'body'       => $body
        );
    }
}
<?php

namespace Guzzle\Parser\Message;

/**
 * HTTP message parser interface used to parse HTTP messages into an array
 */
interface MessageParserInterface
{
    /**
     * Parse an HTTP request message into an associative array of parts.
     *
     * @param string $message HTTP request to parse
     *
     * @return array|bool Returns false if the message is invalid
     */
    public function parseRequest($message);

    /**
     * Parse an HTTP response message into an associative array of parts.
     *
     * @param string $message HTTP response to parse
     *
     * @return array|bool Returns false if the message is invalid
     */
    public function parseResponse($message);
}
<?php

namespace Guzzle\Parser\Message;

/**
 * Pecl HTTP message parser
 */
class PeclHttpMessageParser extends AbstractMessageParser
{
    public function parseRequest($message)
    {
        if (!$message) {
            return false;
        }

        $parts = http_parse_message($message);

        $parsed = array(
            'method'   => $parts->requestMethod,
            'protocol' => 'HTTP',
            'version'  => number_format($parts->httpVersion, 1),
            'headers'  => $parts->headers,
            'body'     => $parts->body
        );

        $parsed['request_url'] = $this->getUrlPartsFromMessage($parts->requestUrl, $parsed);

        return $parsed;
    }

    public function parseResponse($message)
    {
        if (!$message) {
            return false;
        }

        $parts = http_parse_message($message);

        return array(
            'protocol'      => 'HTTP',
            'version'       => number_format($parts->httpVersion, 1),
            'code'          => $parts->responseCode,
            'reason_phrase' => $parts->responseStatus,
            'headers'       => $parts->headers,
            'body'          => $parts->body
        );
    }
}
<?php

namespace Guzzle\Parser;

/**
 * Registry of parsers used by the application
 */
class ParserRegistry
{
    /** @var ParserRegistry Singleton instance */
    protected static $instance;

    /** @var array Array of parser instances */
    protected $instances = array();

    /** @var array Mapping of parser name to default class */
    protected $mapping = array(
        'message'      => 'Guzzle\\Parser\\Message\\MessageParser',
        'cookie'       => 'Guzzle\\Parser\\Cookie\\CookieParser',
        'url'          => 'Guzzle\\Parser\\Url\\UrlParser',
        'uri_template' => 'Guzzle\\Parser\\UriTemplate\\UriTemplate',
    );

    /**
     * @return self
     * @codeCoverageIgnore
     */
    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new static;
        }

        return self::$instance;
    }

    public function __construct()
    {
        // Use the PECL URI template parser if available
        if (extension_loaded('uri_template')) {
            $this->mapping['uri_template'] = 'Guzzle\\Parser\\UriTemplate\\PeclUriTemplate';
        }
    }

    /**
     * Get a parser by name from an instance
     *
     * @param string $name Name of the parser to retrieve
     *
     * @return mixed|null
     */
    public function getParser($name)
    {
        if (!isset($this->instances[$name])) {
            if (!isset($this->mapping[$name])) {
                return null;
            }
            $class = $this->mapping[$name];
            $this->instances[$name] = new $class();
        }

        return $this->instances[$name];
    }

    /**
     * Register a custom parser by name with the register
     *
     * @param string $name   Name or handle of the parser to register
     * @param mixed  $parser Instantiated parser to register
     */
    public function registerParser($name, $parser)
    {
        $this->instances[$name] = $parser;
    }
}
<?php

namespace Guzzle\Parser\UriTemplate;

use Guzzle\Common\Exception\RuntimeException;

/**
 * Expands URI templates using the uri_template pecl extension (pecl install uri_template-beta)
 *
 * @link http://pecl.php.net/package/uri_template
 * @link https://github.com/ioseb/uri-template
 */
class PeclUriTemplate implements UriTemplateInterface
{
    public function __construct()
    {
        if (!extension_loaded('uri_template')) {
            throw new RuntimeException('uri_template PECL extension must be installed to use PeclUriTemplate');
        }
    }

    public function expand($template, array $variables)
    {
        return uri_template($template, $variables);
    }
}
<?php

namespace Guzzle\Parser\UriTemplate;

/**
 * Expands URI templates using an array of variables
 *
 * @link http://tools.ietf.org/html/draft-gregorio-uritemplate-08
 */
class UriTemplate implements UriTemplateInterface
{
    const DEFAULT_PATTERN = '/\{([^\}]+)\}/';

    /** @var string URI template */
    private $template;

    /** @var array Variables to use in the template expansion */
    private $variables;

    /** @var string Regex used to parse expressions */
    private $regex = self::DEFAULT_PATTERN;

    /** @var array Hash for quick operator lookups */
    private static $operatorHash = array(
        '+' => true, '#' => true, '.' => true, '/' => true, ';' => true, '?' => true, '&' => true
    );

    /** @var array Delimiters */
    private static $delims = array(
        ':', '/', '?', '#', '[', ']', '@', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '='
    );

    /** @var array Percent encoded delimiters */
    private static $delimsPct = array(
        '%3A', '%2F', '%3F', '%23', '%5B', '%5D', '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C',
        '%3B', '%3D'
    );

    public function expand($template, array $variables)
    {
        if ($this->regex == self::DEFAULT_PATTERN && false === strpos($template, '{')) {
            return $template;
        }

        $this->template = $template;
        $this->variables = $variables;

        return preg_replace_callback($this->regex, array($this, 'expandMatch'), $this->template);
    }

    /**
     * Set the regex patten used to expand URI templates
     *
     * @param string $regexPattern
     */
    public function setRegex($regexPattern)
    {
        $this->regex = $regexPattern;
    }

    /**
     * Parse an expression into parts
     *
     * @param string $expression Expression to parse
     *
     * @return array Returns an associative array of parts
     */
    private function parseExpression($expression)
    {
        // Check for URI operators
        $operator = '';

        if (isset(self::$operatorHash[$expression[0]])) {
            $operator = $expression[0];
            $expression = substr($expression, 1);
        }

        $values = explode(',', $expression);
        foreach ($values as &$value) {
            $value = trim($value);
            $varspec = array();
            $substrPos = strpos($value, ':');
            if ($substrPos) {
                $varspec['value'] = substr($value, 0, $substrPos);
                $varspec['modifier'] = ':';
                $varspec['position'] = (int) substr($value, $substrPos + 1);
            } elseif (substr($value, -1) == '*') {
                $varspec['modifier'] = '*';
                $varspec['value'] = substr($value, 0, -1);
            } else {
                $varspec['value'] = (string) $value;
                $varspec['modifier'] = '';
            }
            $value = $varspec;
        }

        return array(
            'operator' => $operator,
            'values'   => $values
        );
    }

    /**
     * Process an expansion
     *
     * @param array $matches Matches met in the preg_replace_callback
     *
     * @return string Returns the replacement string
     */
    private function expandMatch(array $matches)
    {
        static $rfc1738to3986 = array(
            '+'   => '%20',
            '%7e' => '~'
        );

        $parsed = self::parseExpression($matches[1]);
        $replacements = array();

        $prefix = $parsed['operator'];
        $joiner = $parsed['operator'];
        $useQueryString = false;
        if ($parsed['operator'] == '?') {
            $joiner = '&';
            $useQueryString = true;
        } elseif ($parsed['operator'] == '&') {
            $useQueryString = true;
        } elseif ($parsed['operator'] == '#') {
            $joiner = ',';
        } elseif ($parsed['operator'] == ';') {
            $useQueryString = true;
        } elseif ($parsed['operator'] == '' || $parsed['operator'] == '+') {
            $joiner = ',';
            $prefix = '';
        }

        foreach ($parsed['values'] as $value) {

            if (!array_key_exists($value['value'], $this->variables) || $this->variables[$value['value']] === null) {
                continue;
            }

            $variable = $this->variables[$value['value']];
            $actuallyUseQueryString = $useQueryString;
            $expanded = '';

            if (is_array($variable)) {

                $isAssoc = $this->isAssoc($variable);
                $kvp = array();
                foreach ($variable as $key => $var) {

                    if ($isAssoc) {
                        $key = rawurlencode($key);
                        $isNestedArray = is_array($var);
                    } else {
                        $isNestedArray = false;
                    }

                    if (!$isNestedArray) {
                        $var = rawurlencode($var);
                        if ($parsed['operator'] == '+' || $parsed['operator'] == '#') {
                            $var = $this->decodeReserved($var);
                        }
                    }

                    if ($value['modifier'] == '*') {
                        if ($isAssoc) {
                            if ($isNestedArray) {
                                // Nested arrays must allow for deeply nested structures
                                $var = strtr(http_build_query(array($key => $var)), $rfc1738to3986);
                            } else {
                                $var = $key . '=' . $var;
                            }
                        } elseif ($key > 0 && $actuallyUseQueryString) {
                            $var = $value['value'] . '=' . $var;
                        }
                    }

                    $kvp[$key] = $var;
                }

                if (empty($variable)) {
                    $actuallyUseQueryString = false;
                } elseif ($value['modifier'] == '*') {
                    $expanded = implode($joiner, $kvp);
                    if ($isAssoc) {
                        // Don't prepend the value name when using the explode modifier with an associative array
                        $actuallyUseQueryString = false;
                    }
                } else {
                    if ($isAssoc) {
                        // When an associative array is encountered and the explode modifier is not set, then the
                        // result must be a comma separated list of keys followed by their respective values.
                        foreach ($kvp as $k => &$v) {
                            $v = $k . ',' . $v;
                        }
                    }
                    $expanded = implode(',', $kvp);
                }

            } else {
                if ($value['modifier'] == ':') {
                    $variable = substr($variable, 0, $value['position']);
                }
                $expanded = rawurlencode($variable);
                if ($parsed['operator'] == '+' || $parsed['operator'] == '#') {
                    $expanded = $this->decodeReserved($expanded);
                }
            }

            if ($actuallyUseQueryString) {
                if (!$expanded && $joiner != '&') {
                    $expanded = $value['value'];
                } else {
                    $expanded = $value['value'] . '=' . $expanded;
                }
            }

            $replacements[] = $expanded;
        }

        $ret = implode($joiner, $replacements);
        if ($ret && $prefix) {
            return $prefix . $ret;
        }

        return $ret;
    }

    /**
     * Determines if an array is associative
     *
     * @param array $array Array to check
     *
     * @return bool
     */
    private function isAssoc(array $array)
    {
        return (bool) count(array_filter(array_keys($array), 'is_string'));
    }

    /**
     * Removes percent encoding on reserved characters (used with + and # modifiers)
     *
     * @param string $string String to fix
     *
     * @return string
     */
    private function decodeReserved($string)
    {
        return str_replace(self::$delimsPct, self::$delims, $string);
    }
}
<?php

namespace Guzzle\Parser\UriTemplate;

/**
 * Expands URI templates using an array of variables
 *
 * @link http://tools.ietf.org/html/rfc6570
 */
interface UriTemplateInterface
{
    /**
     * Expand the URI template using the supplied variables
     *
     * @param string $template  URI Template to expand
     * @param array  $variables Variables to use with the expansion
     *
     * @return string Returns the expanded template
     */
    public function expand($template, array $variables);
}
<?php

namespace Guzzle\Parser\Url;

use Guzzle\Common\Version;

/**
 * Parses URLs into parts using PHP's built-in parse_url() function
 * @deprecated Just use parse_url. UTF-8 characters should be percent encoded anyways.
 * @codeCoverageIgnore
 */
class UrlParser implements UrlParserInterface
{
    /** @var bool Whether or not to work with UTF-8 strings */
    protected $utf8 = false;

    /**
     * Set whether or not to attempt to handle UTF-8 strings (still WIP)
     *
     * @param bool $utf8 Set to TRUE to handle UTF string
     */
    public function setUtf8Support($utf8)
    {
        $this->utf8 = $utf8;
    }

    public function parseUrl($url)
    {
        Version::warn(__CLASS__ . ' is deprecated. Just use parse_url()');

        static $defaults = array('scheme' => null, 'host' => null, 'path' => null, 'port' => null, 'query' => null,
            'user' => null, 'pass' => null, 'fragment' => null);

        $parts = parse_url($url);

        // Need to handle query parsing specially for UTF-8 requirements
        if ($this->utf8 && isset($parts['query'])) {
            $queryPos = strpos($url, '?');
            if (isset($parts['fragment'])) {
                $parts['query'] = substr($url, $queryPos + 1, strpos($url, '#') - $queryPos - 1);
            } else {
                $parts['query'] = substr($url, $queryPos + 1);
            }
        }

        return $parts + $defaults;
    }
}
<?php

namespace Guzzle\Parser\Url;

/**
 * URL parser interface
 */
interface UrlParserInterface
{
    /**
     * Parse a URL using special handling for a subset of UTF-8 characters in the query string if needed.
     *
     * @param string $url URL to parse
     *
     * @return array Returns an array identical to what is returned from parse_url().  When an array key is missing from
     *               this array, you must fill it in with NULL to avoid warnings in calling code.
     */
    public function parseUrl($url);
}
<?php

namespace Guzzle\Plugin\Async;

use Guzzle\Common\Event;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\CurlException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Sends requests but does not wait for the response
 */
class AsyncPlugin implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
            'request.before_send'    => 'onBeforeSend',
            'request.exception'      => 'onRequestTimeout',
            'request.sent'           => 'onRequestSent',
            'curl.callback.progress' => 'onCurlProgress'
        );
    }

    /**
     * Event used to ensure that progress callback are emitted from the curl handle's request mediator.
     *
     * @param Event $event
     */
    public function onBeforeSend(Event $event)
    {
        // Ensure that progress callbacks are dispatched
        $event['request']->getCurlOptions()->set('progress', true);
    }

    /**
     * Event emitted when a curl progress function is called. When the amount of data uploaded == the amount of data to
     * upload OR any bytes have been downloaded, then time the request out after 1ms because we're done with
     * transmitting the request, and tell curl not download a body.
     *
     * @param Event $event
     */
    public function onCurlProgress(Event $event)
    {
        if ($event['handle'] &&
            ($event['downloaded'] || (isset($event['uploaded']) && $event['upload_size'] === $event['uploaded']))
        ) {
            // Timeout after 1ms
            curl_setopt($event['handle'], CURLOPT_TIMEOUT_MS, 1);
            // Even if the response is quick, tell curl not to download the body.
            // - Note that we can only perform this shortcut if the request transmitted a body so as to ensure that the
            //   request method is not converted to a HEAD request before the request was sent via curl.
            if ($event['uploaded']) {
                curl_setopt($event['handle'], CURLOPT_NOBODY, true);
            }
        }
    }

    /**
     * Event emitted when a curl exception occurs. Ignore the exception and set a mock response.
     *
     * @param Event $event
     */
    public function onRequestTimeout(Event $event)
    {
        if ($event['exception'] instanceof CurlException) {
            $event['request']->setResponse(new Response(200, array(
                'X-Guzzle-Async' => 'Did not wait for the response'
            )));
        }
    }

    /**
     * Event emitted when a request completes because it took less than 1ms. Add an X-Guzzle-Async header to notify the
     * caller that there is no body in the message.
     *
     * @param Event $event
     */
    public function onRequestSent(Event $event)
    {
        // Let the caller know this was meant to be async
        $event['request']->getResponse()->setHeader('X-Guzzle-Async', 'Did not wait for the response');
    }
}
<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Abstract backoff strategy that allows for a chain of responsibility
 */
abstract class AbstractBackoffStrategy implements BackoffStrategyInterface
{
    /** @var AbstractBackoffStrategy Next strategy in the chain */
    protected $next;

    /** @param AbstractBackoffStrategy $next Next strategy in the chain */
    public function setNext(AbstractBackoffStrategy $next)
    {
        $this->next = $next;
    }

    /**
     * Get the next backoff strategy in the chain
     *
     * @return AbstractBackoffStrategy|null
     */
    public function getNext()
    {
        return $this->next;
    }

    public function getBackoffPeriod(
        $retries,
        RequestInterface $request,
        Response $response = null,
        HttpException $e = null
    ) {
        $delay = $this->getDelay($retries, $request, $response, $e);
        if ($delay === false) {
            // The strategy knows that this must not be retried
            return false;
        } elseif ($delay === null) {
            // If the strategy is deferring a decision and the next strategy will not make a decision then return false
            return !$this->next || !$this->next->makesDecision()
                ? false
                : $this->next->getBackoffPeriod($retries, $request, $response, $e);
        } elseif ($delay === true) {
            // if the strategy knows that it must retry but is deferring to the next to determine the delay
            if (!$this->next) {
                return 0;
            } else {
                $next = $this->next;
                while ($next->makesDecision() && $next->getNext()) {
                    $next = $next->getNext();
                }
                return !$next->makesDecision() ? $next->getBackoffPeriod($retries, $request, $response, $e) : 0;
            }
        } else {
            return $delay;
        }
    }

    /**
     * Check if the strategy does filtering and makes decisions on whether or not to retry.
     *
     * Strategies that return false will never retry if all of the previous strategies in a chain defer on a backoff
     * decision.
     *
     * @return bool
     */
    abstract public function makesDecision();

    /**
     * Implement the concrete strategy
     *
     * @param int              $retries  Number of retries of the request
     * @param RequestInterface $request  Request that was sent
     * @param Response         $response Response that was received. Note that there may not be a response
     * @param HttpException    $e        Exception that was encountered if any
     *
     * @return bool|int|null Returns false to not retry or the number of seconds to delay between retries. Return true
     *                       or null to defer to the next strategy if available, and if not, return 0.
     */
    abstract protected function getDelay(
        $retries,
        RequestInterface $request,
        Response $response = null,
        HttpException $e = null
    );
}
<?php

namespace Guzzle\Plugin\Backoff;

/**
 * Strategy used to retry when certain error codes are encountered
 */
abstract class AbstractErrorCodeBackoffStrategy extends AbstractBackoffStrategy
{
    /** @var array Default cURL errors to retry */
    protected static $defaultErrorCodes = array();

    /** @var array Error codes that can be retried */
    protected $errorCodes;

    /**
     * @param array                    $codes Array of codes that should be retried
     * @param BackoffStrategyInterface $next  The optional next strategy
     */
    public function __construct(array $codes = null, BackoffStrategyInterface $next = null)
    {
        $this->errorCodes = array_fill_keys($codes ?: static::$defaultErrorCodes, 1);
        $this->next = $next;
    }

    /**
     * Get the default failure codes to retry
     *
     * @return array
     */
    public static function getDefaultFailureCodes()
    {
        return static::$defaultErrorCodes;
    }

    public function makesDecision()
    {
        return true;
    }
}
<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Common\Event;
use Guzzle\Log\LogAdapterInterface;
use Guzzle\Log\MessageFormatter;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Logs backoff retries triggered from the BackoffPlugin
 *
 * Format your log messages using a template that can contain template substitutions found in {@see MessageFormatter}.
 * In addition to the default template substitutions, there is also:
 *
 * - retries: The number of times the request has been retried
 * - delay:   The amount of time the request is being delayed
 */
class BackoffLogger implements EventSubscriberInterface
{
    /** @var string Default log message template */
    const DEFAULT_FORMAT = '[{ts}] {method} {url} - {code} {phrase} - Retries: {retries}, Delay: {delay}, Time: {connect_time}, {total_time}, cURL: {curl_code} {curl_error}';

    /** @var LogAdapterInterface Logger used to log retries */
    protected $logger;

    /** @var MessageFormatter Formatter used to format log messages */
    protected $formatter;

    /**
     * @param LogAdapterInterface $logger    Logger used to log the retries
     * @param MessageFormatter    $formatter Formatter used to format log messages
     */
    public function __construct(LogAdapterInterface $logger, MessageFormatter $formatter = null)
    {
        $this->logger = $logger;
        $this->formatter = $formatter ?: new MessageFormatter(self::DEFAULT_FORMAT);
    }

    public static function getSubscribedEvents()
    {
        return array(BackoffPlugin::RETRY_EVENT => 'onRequestRetry');
    }

    /**
     * Set the template to use for logging
     *
     * @param string $template Log message template
     *
     * @return self
     */
    public function setTemplate($template)
    {
        $this->formatter->setTemplate($template);

        return $this;
    }

    /**
     * Called when a request is being retried
     *
     * @param Event $event Event emitted
     */
    public function onRequestRetry(Event $event)
    {
        $this->logger->log($this->formatter->format(
            $event['request'],
            $event['response'],
            $event['handle'],
            array(
                'retries' => $event['retries'],
                'delay'   => $event['delay']
            )
        ));
    }
}
<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Common\Event;
use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Curl\CurlMultiInterface;
use Guzzle\Http\Exception\CurlException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Plugin to automatically retry failed HTTP requests using a backoff strategy
 */
class BackoffPlugin extends AbstractHasDispatcher implements EventSubscriberInterface
{
    const DELAY_PARAM = CurlMultiInterface::BLOCKING;
    const RETRY_PARAM = 'plugins.backoff.retry_count';
    const RETRY_EVENT = 'plugins.backoff.retry';

    /** @var BackoffStrategyInterface Backoff strategy */
    protected $strategy;

    /**
     * @param BackoffStrategyInterface $strategy The backoff strategy used to determine whether or not to retry and
     *                                           the amount of delay between retries.
     */
    public function __construct(BackoffStrategyInterface $strategy = null)
    {
        $this->strategy = $strategy;
    }

    /**
     * Retrieve a basic truncated exponential backoff plugin that will retry HTTP errors and cURL errors
     *
     * @param int   $maxRetries Maximum number of retries
     * @param array $httpCodes  HTTP response codes to retry
     * @param array $curlCodes  cURL error codes to retry
     *
     * @return self
     */
    public static function getExponentialBackoff(
        $maxRetries = 3,
        array $httpCodes = null,
        array $curlCodes = null
    ) {
        return new self(new TruncatedBackoffStrategy($maxRetries,
            new HttpBackoffStrategy($httpCodes,
                new CurlBackoffStrategy($curlCodes,
                    new ExponentialBackoffStrategy()
                )
            )
        ));
    }

    public static function getAllEvents()
    {
        return array(self::RETRY_EVENT);
    }

    public static function getSubscribedEvents()
    {
        return array(
            'request.sent'      => 'onRequestSent',
            'request.exception' => 'onRequestSent',
            CurlMultiInterface::POLLING_REQUEST => 'onRequestPoll'
        );
    }

    /**
     * Called when a request has been sent  and isn't finished processing
     *
     * @param Event $event
     */
    public function onRequestSent(Event $event)
    {
        $request = $event['request'];
        $response = $event['response'];
        $exception = $event['exception'];

        $params = $request->getParams();
        $retries = (int) $params->get(self::RETRY_PARAM);
        $delay = $this->strategy->getBackoffPeriod($retries, $request, $response, $exception);

        if ($delay !== false) {
            // Calculate how long to wait until the request should be retried
            $params->set(self::RETRY_PARAM, ++$retries)
                ->set(self::DELAY_PARAM, microtime(true) + $delay);
            // Send the request again
            $request->setState(RequestInterface::STATE_TRANSFER);
            $this->dispatch(self::RETRY_EVENT, array(
                'request'  => $request,
                'response' => $response,
                'handle'   => ($exception && $exception instanceof CurlException) ? $exception->getCurlHandle() : null,
                'retries'  => $retries,
                'delay'    => $delay
            ));
        }
    }

    /**
     * Called when a request is polling in the curl multi object
     *
     * @param Event $event
     */
    public function onRequestPoll(Event $event)
    {
        $request = $event['request'];
        $delay = $request->getParams()->get(self::DELAY_PARAM);

        // If the duration of the delay has passed, retry the request using the pool
        if (null !== $delay && microtime(true) >= $delay) {
            // Remove the request from the pool and then add it back again. This is required for cURL to know that we
            // want to retry sending the easy handle.
            $request->getParams()->remove(self::DELAY_PARAM);
            // Rewind the request body if possible
            if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()) {
                $request->getBody()->seek(0);
            }
            $multi = $event['curl_multi'];
            $multi->remove($request);
            $multi->add($request);
        }
    }
}
<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Strategy to determine if a request should be retried and how long to delay between retries
 */
interface BackoffStrategyInterface
{
    /**
     * Get the amount of time to delay in seconds before retrying a request
     *
     * @param int              $retries  Number of retries of the request
     * @param RequestInterface $request  Request that was sent
     * @param Response         $response Response that was received. Note that there may not be a response
     * @param HttpException    $e        Exception that was encountered if any
     *
     * @return bool|int Returns false to not retry or the number of seconds to delay between retries
     */
    public function getBackoffPeriod(
        $retries,
        RequestInterface $request,
        Response $response = null,
        HttpException $e = null
    );
}
<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Strategy that will invoke a closure to determine whether or not to retry with a delay
 */
class CallbackBackoffStrategy extends AbstractBackoffStrategy
{
    /** @var \Closure|array|mixed Callable method to invoke */
    protected $callback;

    /** @var bool Whether or not this strategy makes a retry decision */
    protected $decision;

    /**
     * @param \Closure|array|mixed     $callback Callable method to invoke
     * @param bool                     $decision Set to true if this strategy makes a backoff decision
     * @param BackoffStrategyInterface $next     The optional next strategy
     *
     * @throws InvalidArgumentException
     */
    public function __construct($callback, $decision, BackoffStrategyInterface $next = null)
    {
        if (!is_callable($callback)) {
            throw new InvalidArgumentException('The callback must be callable');
        }
        $this->callback = $callback;
        $this->decision = (bool) $decision;
        $this->next = $next;
    }

    public function makesDecision()
    {
        return $this->decision;
    }

    protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
    {
        return call_user_func($this->callback, $retries, $request, $response, $e);
    }
}
<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Will retry the request using the same amount of delay for each retry.
 *
 * Warning: If no decision making strategies precede this strategy in the the chain, then all requests will be retried
 */
class ConstantBackoffStrategy extends AbstractBackoffStrategy
{
    /** @var int Amount of time for each delay */
    protected $delay;

    /** @param int $delay Amount of time to delay between each additional backoff */
    public function __construct($delay)
    {
        $this->delay = $delay;
    }

    public function makesDecision()
    {
        return false;
    }

    protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
    {
        return $this->delay;
    }
}
<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;
use Guzzle\Http\Exception\CurlException;

/**
 * Strategy used to retry when certain cURL error codes are encountered.
 */
class CurlBackoffStrategy extends AbstractErrorCodeBackoffStrategy
{
    /** @var array Default cURL errors to retry */
    protected static $defaultErrorCodes = array(
        CURLE_COULDNT_RESOLVE_HOST, CURLE_COULDNT_CONNECT, CURLE_PARTIAL_FILE, CURLE_WRITE_ERROR, CURLE_READ_ERROR,
        CURLE_OPERATION_TIMEOUTED, CURLE_SSL_CONNECT_ERROR, CURLE_HTTP_PORT_FAILED, CURLE_GOT_NOTHING,
        CURLE_SEND_ERROR, CURLE_RECV_ERROR
    );

    protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
    {
        if ($e && $e instanceof CurlException) {
            return isset($this->errorCodes[$e->getErrorNo()]) ? true : null;
        }
    }
}
<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Implements an exponential backoff retry strategy.
 *
 * Warning: If no decision making strategies precede this strategy in the the chain, then all requests will be retried
 */
class ExponentialBackoffStrategy extends AbstractBackoffStrategy
{
    public function makesDecision()
    {
        return false;
    }

    protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
    {
        return (int) pow(2, $retries);
    }
}
<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Strategy used to retry HTTP requests based on the response code.
 *
 * Retries 500 and 503 error by default.
 */
class HttpBackoffStrategy extends AbstractErrorCodeBackoffStrategy
{
    /** @var array Default cURL errors to retry */
    protected static $defaultErrorCodes = array(500, 503);

    protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
    {
        if ($response) {
            //Short circuit the rest of the checks if it was successful
            if ($response->isSuccessful()) {
                return false;
            } else {
                return isset($this->errorCodes[$response->getStatusCode()]) ? true : null;
            }
        }
    }
}
<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Implements a linear backoff retry strategy.
 *
 * Warning: If no decision making strategies precede this strategy in the the chain, then all requests will be retried
 */
class LinearBackoffStrategy extends AbstractBackoffStrategy
{
    /** @var int Amount of time to progress each delay */
    protected $step;

    /**
     * @param int $step Amount of time to increase the delay each additional backoff
     */
    public function __construct($step = 1)
    {
        $this->step = $step;
    }

    public function makesDecision()
    {
        return false;
    }

    protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
    {
        return $retries * $this->step;
    }
}
<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Strategy used to retry HTTP requests when the response's reason phrase matches one of the registered phrases.
 */
class ReasonPhraseBackoffStrategy extends AbstractErrorCodeBackoffStrategy
{
    public function makesDecision()
    {
        return true;
    }

    protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
    {
        if ($response) {
            return isset($this->errorCodes[$response->getReasonPhrase()]) ? true : null;
        }
    }
}
<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Strategy that will not retry more than a certain number of times.
 */
class TruncatedBackoffStrategy extends AbstractBackoffStrategy
{
    /** @var int Maximum number of retries per request */
    protected $max;

    /**
     * @param int                      $maxRetries Maximum number of retries per request
     * @param BackoffStrategyInterface $next The optional next strategy
     */
    public function __construct($maxRetries, BackoffStrategyInterface $next = null)
    {
        $this->max = $maxRetries;
        $this->next = $next;
    }

    public function makesDecision()
    {
        return true;
    }

    protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
    {
        return $retries < $this->max ? null : false;
    }
}
<?php

namespace Guzzle\Plugin\Cache;

\Guzzle\Common\Version::warn('Guzzle\Plugin\Cache\CacheKeyProviderInterface is no longer used');

/**
 * @deprecated This is no longer used
 * @codeCoverageIgnore
 */
interface CacheKeyProviderInterface {}
<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Cache\CacheAdapterFactory;
use Guzzle\Cache\CacheAdapterInterface;
use Guzzle\Common\Event;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Version;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Cache\DoctrineCacheAdapter;
use Guzzle\Http\Exception\CurlException;
use Doctrine\Common\Cache\ArrayCache;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Plugin to enable the caching of GET and HEAD requests.  Caching can be done on all requests passing through this
 * plugin or only after retrieving resources with cacheable response headers.
 *
 * This is a simple implementation of RFC 2616 and should be considered a private transparent proxy cache, meaning
 * authorization and private data can be cached.
 *
 * It also implements RFC 5861's `stale-if-error` Cache-Control extension, allowing stale cache responses to be used
 * when an error is encountered (such as a `500 Internal Server Error` or DNS failure).
 */
class CachePlugin implements EventSubscriberInterface
{
    /** @var RevalidationInterface Cache revalidation strategy */
    protected $revalidation;

    /** @var CanCacheStrategyInterface Object used to determine if a request can be cached */
    protected $canCache;

    /** @var CacheStorageInterface $cache Object used to cache responses */
    protected $storage;

    /** @var bool */
    protected $autoPurge;

    /**
     * @param array|CacheAdapterInterface|CacheStorageInterface $options Array of options for the cache plugin,
     *     cache adapter, or cache storage object.
     *     - CacheStorageInterface storage:      Adapter used to cache responses
     *     - RevalidationInterface revalidation: Cache revalidation strategy
     *     - CanCacheInterface     can_cache:    Object used to determine if a request can be cached
     *     - bool                  auto_purge    Set to true to automatically PURGE resources when non-idempotent
     *                                           requests are sent to a resource. Defaults to false.
     * @throws InvalidArgumentException if no cache is provided and Doctrine cache is not installed
     */
    public function __construct($options = null)
    {
        if (!is_array($options)) {
            if ($options instanceof CacheAdapterInterface) {
                $options = array('storage' => new DefaultCacheStorage($options));
            } elseif ($options instanceof CacheStorageInterface) {
                $options = array('storage' => $options);
            } elseif ($options) {
                $options = array('storage' => new DefaultCacheStorage(CacheAdapterFactory::fromCache($options)));
            } elseif (!class_exists('Doctrine\Common\Cache\ArrayCache')) {
                // @codeCoverageIgnoreStart
                throw new InvalidArgumentException('No cache was provided and Doctrine is not installed');
                // @codeCoverageIgnoreEnd
            }
        }

        $this->autoPurge = isset($options['auto_purge']) ? $options['auto_purge'] : false;

        // Add a cache storage if a cache adapter was provided
        $this->storage = isset($options['storage'])
            ? $options['storage']
            : new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache()));

        if (!isset($options['can_cache'])) {
            $this->canCache = new DefaultCanCacheStrategy();
        } else {
            $this->canCache = is_callable($options['can_cache'])
                ? new CallbackCanCacheStrategy($options['can_cache'])
                : $options['can_cache'];
        }

        // Use the provided revalidation strategy or the default
        $this->revalidation = isset($options['revalidation'])
            ? $options['revalidation']
            : new DefaultRevalidation($this->storage, $this->canCache);
    }

    public static function getSubscribedEvents()
    {
        return array(
            'request.before_send' => array('onRequestBeforeSend', -255),
            'request.sent'        => array('onRequestSent', 255),
            'request.error'       => array('onRequestError', 0),
            'request.exception'   => array('onRequestException', 0),
        );
    }

    /**
     * Check if a response in cache will satisfy the request before sending
     *
     * @param Event $event
     */
    public function onRequestBeforeSend(Event $event)
    {
        $request = $event['request'];
        $request->addHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION));

        if (!$this->canCache->canCacheRequest($request)) {
            switch ($request->getMethod()) {
                case 'PURGE':
                    $this->purge($request);
                    $request->setResponse(new Response(200, array(), 'purged'));
                    break;
                case 'PUT':
                case 'POST':
                case 'DELETE':
                case 'PATCH':
                    if ($this->autoPurge) {
                        $this->purge($request);
                    }
            }
            return;
        }

        if ($response = $this->storage->fetch($request)) {
            $params = $request->getParams();
            $params['cache.lookup'] = true;
            $response->setHeader(
                'Age',
                time() - strtotime($response->getDate() ? : $response->getLastModified() ?: 'now')
            );
            // Validate that the response satisfies the request
            if ($this->canResponseSatisfyRequest($request, $response)) {
                if (!isset($params['cache.hit'])) {
                    $params['cache.hit'] = true;
                }
                $request->setResponse($response);
            }
        }
    }

    /**
     * If possible, store a response in cache after sending
     *
     * @param Event $event
     */
    public function onRequestSent(Event $event)
    {
        $request = $event['request'];
        $response = $event['response'];

        if ($request->getParams()->get('cache.hit') === null &&
            $this->canCache->canCacheRequest($request) &&
            $this->canCache->canCacheResponse($response)
        ) {
            $this->storage->cache($request, $response);
        }

        $this->addResponseHeaders($request, $response);
    }

    /**
     * If possible, return a cache response on an error
     *
     * @param Event $event
     */
    public function onRequestError(Event $event)
    {
        $request = $event['request'];

        if (!$this->canCache->canCacheRequest($request)) {
            return;
        }

        if ($response = $this->storage->fetch($request)) {
            $response->setHeader(
                'Age',
                time() - strtotime($response->getLastModified() ? : $response->getDate() ?: 'now')
            );

            if ($this->canResponseSatisfyFailedRequest($request, $response)) {
                $request->getParams()->set('cache.hit', 'error');
                $this->addResponseHeaders($request, $response);
                $event['response'] = $response;
                $event->stopPropagation();
            }
        }
    }

    /**
     * If possible, set a cache response on a cURL exception
     *
     * @param Event $event
     *
     * @return null
     */
    public function onRequestException(Event $event)
    {
        if (!$event['exception'] instanceof CurlException) {
            return;
        }

        $request = $event['request'];
        if (!$this->canCache->canCacheRequest($request)) {
            return;
        }

        if ($response = $this->storage->fetch($request)) {
            $response->setHeader('Age', time() - strtotime($response->getDate() ? : 'now'));
            if (!$this->canResponseSatisfyFailedRequest($request, $response)) {
                return;
            }
            $request->getParams()->set('cache.hit', 'error');
            $request->setResponse($response);
            $this->addResponseHeaders($request, $response);
            $event->stopPropagation();
        }
    }

    /**
     * Check if a cache response satisfies a request's caching constraints
     *
     * @param RequestInterface $request  Request to validate
     * @param Response         $response Response to validate
     *
     * @return bool
     */
    public function canResponseSatisfyRequest(RequestInterface $request, Response $response)
    {
        $responseAge = $response->calculateAge();
        $reqc = $request->getHeader('Cache-Control');
        $resc = $response->getHeader('Cache-Control');

        // Check the request's max-age header against the age of the response
        if ($reqc && $reqc->hasDirective('max-age') &&
            $responseAge > $reqc->getDirective('max-age')) {
            return false;
        }

        // Check the response's max-age header
        if ($response->isFresh() === false) {
            $maxStale = $reqc ? $reqc->getDirective('max-stale') : null;
            if (null !== $maxStale) {
                if ($maxStale !== true && $response->getFreshness() < (-1 * $maxStale)) {
                    return false;
                }
            } elseif ($resc && $resc->hasDirective('max-age')
                && $responseAge > $resc->getDirective('max-age')
            ) {
                return false;
            }
        }

        if ($this->revalidation->shouldRevalidate($request, $response)) {
            try {
                return $this->revalidation->revalidate($request, $response);
            } catch (CurlException $e) {
                $request->getParams()->set('cache.hit', 'error');
                return $this->canResponseSatisfyFailedRequest($request, $response);
            }
        }

        return true;
    }

    /**
     * Check if a cache response satisfies a failed request's caching constraints
     *
     * @param RequestInterface $request  Request to validate
     * @param Response         $response Response to validate
     *
     * @return bool
     */
    public function canResponseSatisfyFailedRequest(RequestInterface $request, Response $response)
    {
        $reqc = $request->getHeader('Cache-Control');
        $resc = $response->getHeader('Cache-Control');
        $requestStaleIfError = $reqc ? $reqc->getDirective('stale-if-error') : null;
        $responseStaleIfError = $resc ? $resc->getDirective('stale-if-error') : null;

        if (!$requestStaleIfError && !$responseStaleIfError) {
            return false;
        }

        if (is_numeric($requestStaleIfError) && $response->getAge() - $response->getMaxAge() > $requestStaleIfError) {
            return false;
        }

        if (is_numeric($responseStaleIfError) && $response->getAge() - $response->getMaxAge() > $responseStaleIfError) {
            return false;
        }

        return true;
    }

    /**
     * Purge all cache entries for a given URL
     *
     * @param string $url URL to purge
     */
    public function purge($url)
    {
        // BC compatibility with previous version that accepted a Request object
        $url = $url instanceof RequestInterface ? $url->getUrl() : $url;
        $this->storage->purge($url);
    }

    /**
     * Add the plugin's headers to a response
     *
     * @param RequestInterface $request  Request
     * @param Response         $response Response to add headers to
     */
    protected function addResponseHeaders(RequestInterface $request, Response $response)
    {
        $params = $request->getParams();
        $response->setHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION));

        $lookup = ($params['cache.lookup'] === true ? 'HIT' : 'MISS') . ' from GuzzleCache';
        if ($header = $response->getHeader('X-Cache-Lookup')) {
            // Don't add duplicates
            $values = $header->toArray();
            $values[] = $lookup;
            $response->setHeader('X-Cache-Lookup', array_unique($values));
        } else {
            $response->setHeader('X-Cache-Lookup', $lookup);
        }

        if ($params['cache.hit'] === true) {
            $xcache = 'HIT from GuzzleCache';
        } elseif ($params['cache.hit'] == 'error') {
            $xcache = 'HIT_ERROR from GuzzleCache';
        } else {
            $xcache = 'MISS from GuzzleCache';
        }

        if ($header = $response->getHeader('X-Cache')) {
            // Don't add duplicates
            $values = $header->toArray();
            $values[] = $xcache;
            $response->setHeader('X-Cache', array_unique($values));
        } else {
            $response->setHeader('X-Cache', $xcache);
        }

        if ($response->isFresh() === false) {
            $response->addHeader('Warning', sprintf('110 GuzzleCache/%s "Response is stale"', Version::VERSION));
            if ($params['cache.hit'] === 'error') {
                $response->addHeader('Warning', sprintf('111 GuzzleCache/%s "Revalidation failed"', Version::VERSION));
            }
        }
    }
}
<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Interface used to cache HTTP requests
 */
interface CacheStorageInterface
{
    /**
     * Get a Response from the cache for a request
     *
     * @param RequestInterface $request
     *
     * @return null|Response
     */
    public function fetch(RequestInterface $request);

    /**
     * Cache an HTTP request
     *
     * @param RequestInterface $request  Request being cached
     * @param Response         $response Response to cache
     */
    public function cache(RequestInterface $request, Response $response);

    /**
     * Deletes cache entries that match a request
     *
     * @param RequestInterface $request Request to delete from cache
     */
    public function delete(RequestInterface $request);

    /**
     * Purge all cache entries for a given URL
     *
     * @param string $url
     */
    public function purge($url);
}
<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Determines if a request can be cached using a callback
 */
class CallbackCanCacheStrategy extends DefaultCanCacheStrategy
{
    /** @var callable Callback for request */
    protected $requestCallback;

    /** @var callable Callback for response */
    protected $responseCallback;

    /**
     * @param \Closure|array|mixed $requestCallback  Callable method to invoke for requests
     * @param \Closure|array|mixed $responseCallback Callable method to invoke for responses
     *
     * @throws InvalidArgumentException
     */
    public function __construct($requestCallback = null, $responseCallback = null)
    {
        if ($requestCallback && !is_callable($requestCallback)) {
            throw new InvalidArgumentException('Method must be callable');
        }

        if ($responseCallback && !is_callable($responseCallback)) {
            throw new InvalidArgumentException('Method must be callable');
        }

        $this->requestCallback = $requestCallback;
        $this->responseCallback = $responseCallback;
    }

    public function canCacheRequest(RequestInterface $request)
    {
        return $this->requestCallback
            ? call_user_func($this->requestCallback, $request)
            : parent::canCacheRequest($request);
    }

    public function canCacheResponse(Response $response)
    {
        return $this->responseCallback
            ? call_user_func($this->responseCallback, $response)
            : parent::canCacheResponse($response);
    }
}
<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Strategy used to determine if a request can be cached
 */
interface CanCacheStrategyInterface
{
    /**
     * Determine if a request can be cached
     *
     * @param RequestInterface $request Request to determine
     *
     * @return bool
     */
    public function canCacheRequest(RequestInterface $request);

    /**
     * Determine if a response can be cached
     *
     * @param Response $response Response to determine
     *
     * @return bool
     */
    public function canCacheResponse(Response $response);
}
<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;

\Guzzle\Common\Version::warn('Guzzle\Plugin\Cache\DefaultCacheKeyProvider is no longer used');

/**
 * @deprecated This class is no longer used
 * @codeCoverageIgnore
 */
class DefaultCacheKeyProvider implements CacheKeyProviderInterface
{
    public function getCacheKey(RequestInterface $request)
    {
        // See if the key has already been calculated
        $key = $request->getParams()->get(self::CACHE_KEY);

        if (!$key) {

            $cloned = clone $request;
            $cloned->removeHeader('Cache-Control');

            // Check to see how and if the key should be filtered
            foreach (explode(';', $request->getParams()->get(self::CACHE_KEY_FILTER)) as $part) {
                $pieces = array_map('trim', explode('=', $part));
                if (isset($pieces[1])) {
                    foreach (array_map('trim', explode(',', $pieces[1])) as $remove) {
                        if ($pieces[0] == 'header') {
                            $cloned->removeHeader($remove);
                        } elseif ($pieces[0] == 'query') {
                            $cloned->getQuery()->remove($remove);
                        }
                    }
                }
            }

            $raw = (string) $cloned;
            $key = 'GZ' . md5($raw);
            $request->getParams()->set(self::CACHE_KEY, $key)->set(self::CACHE_KEY_RAW, $raw);
        }

        return $key;
    }
}
<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Cache\CacheAdapterFactory;
use Guzzle\Cache\CacheAdapterInterface;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\Message\MessageInterface;
use Guzzle\Http\Message\Request;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Default cache storage implementation
 */
class DefaultCacheStorage implements CacheStorageInterface
{
    /** @var string */
    protected $keyPrefix;

    /** @var CacheAdapterInterface Cache used to store cache data */
    protected $cache;

    /** @var int Default cache TTL */
    protected $defaultTtl;

    /**
     * @param mixed  $cache      Cache used to store cache data
     * @param string $keyPrefix  Provide an optional key prefix to prefix on all cache keys
     * @param int    $defaultTtl Default cache TTL
     */
    public function __construct($cache, $keyPrefix = '', $defaultTtl = 3600)
    {
        $this->cache = CacheAdapterFactory::fromCache($cache);
        $this->defaultTtl = $defaultTtl;
        $this->keyPrefix = $keyPrefix;
    }

    public function cache(RequestInterface $request, Response $response)
    {
        $currentTime = time();

        $overrideTtl = $request->getParams()->get('cache.override_ttl');
        if ($overrideTtl) {
            $ttl = $overrideTtl;
        } else {
            $maxAge = $response->getMaxAge();
            if ($maxAge !== null) {
                $ttl = $maxAge;
            } else {
                $ttl = $this->defaultTtl;
            }
        }

        if ($cacheControl = $response->getHeader('Cache-Control')) {
            $stale = $cacheControl->getDirective('stale-if-error');
            if ($stale === true) {
                $ttl += $ttl;
            } else if (is_numeric($stale)) {
                $ttl += $stale;
            }
        }

        // Determine which manifest key should be used
        $key = $this->getCacheKey($request);
        $persistedRequest = $this->persistHeaders($request);
        $entries = array();

        if ($manifest = $this->cache->fetch($key)) {
            // Determine which cache entries should still be in the cache
            $vary = $response->getVary();
            foreach (unserialize($manifest) as $entry) {
                // Check if the entry is expired
                if ($entry[4] < $currentTime) {
                    continue;
                }
                $entry[1]['vary'] = isset($entry[1]['vary']) ? $entry[1]['vary'] : '';
                if ($vary != $entry[1]['vary'] || !$this->requestsMatch($vary, $entry[0], $persistedRequest)) {
                    $entries[] = $entry;
                }
            }
        }

        // Persist the response body if needed
        $bodyDigest = null;
        if ($response->getBody() && $response->getBody()->getContentLength() > 0) {
            $bodyDigest = $this->getBodyKey($request->getUrl(), $response->getBody());
            $this->cache->save($bodyDigest, (string) $response->getBody(), $ttl);
        }

        array_unshift($entries, array(
            $persistedRequest,
            $this->persistHeaders($response),
            $response->getStatusCode(),
            $bodyDigest,
            $currentTime + $ttl
        ));

        $this->cache->save($key, serialize($entries));
    }

    public function delete(RequestInterface $request)
    {
        $key = $this->getCacheKey($request);
        if ($entries = $this->cache->fetch($key)) {
            // Delete each cached body
            foreach (unserialize($entries) as $entry) {
                if ($entry[3]) {
                    $this->cache->delete($entry[3]);
                }
            }
            $this->cache->delete($key);
        }
    }

    public function purge($url)
    {
        foreach (array('GET', 'HEAD', 'POST', 'PUT', 'DELETE') as $method) {
            $this->delete(new Request($method, $url));
        }
    }

    public function fetch(RequestInterface $request)
    {
        $key = $this->getCacheKey($request);
        if (!($entries = $this->cache->fetch($key))) {
            return null;
        }

        $match = null;
        $headers = $this->persistHeaders($request);
        $entries = unserialize($entries);
        foreach ($entries as $index => $entry) {
            if ($this->requestsMatch(isset($entry[1]['vary']) ? $entry[1]['vary'] : '', $headers, $entry[0])) {
                $match = $entry;
                break;
            }
        }

        if (!$match) {
            return null;
        }

        // Ensure that the response is not expired
        $response = null;
        if ($match[4] < time()) {
            $response = -1;
        } else {
            $response = new Response($match[2], $match[1]);
            if ($match[3]) {
                if ($body = $this->cache->fetch($match[3])) {
                    $response->setBody($body);
                } else {
                    // The response is not valid because the body was somehow deleted
                    $response = -1;
                }
            }
        }

        if ($response === -1) {
            // Remove the entry from the metadata and update the cache
            unset($entries[$index]);
            if ($entries) {
                $this->cache->save($key, serialize($entries));
            } else {
                $this->cache->delete($key);
            }
            return null;
        }

        return $response;
    }

    /**
     * Hash a request URL into a string that returns cache metadata
     *
     * @param RequestInterface $request
     *
     * @return string
     */
    protected function getCacheKey(RequestInterface $request)
    {
        // Allow cache.key_filter to trim down the URL cache key by removing generate query string values (e.g. auth)
        if ($filter = $request->getParams()->get('cache.key_filter')) {
            $url = $request->getUrl(true);
            foreach (explode(',', $filter) as $remove) {
                $url->getQuery()->remove(trim($remove));
            }
        } else {
            $url = $request->getUrl();
        }

        return $this->keyPrefix . md5($request->getMethod() . ' ' . $url);
    }

    /**
     * Create a cache key for a response's body
     *
     * @param string              $url  URL of the entry
     * @param EntityBodyInterface $body Response body
     *
     * @return string
     */
    protected function getBodyKey($url, EntityBodyInterface $body)
    {
        return $this->keyPrefix . md5($url) . $body->getContentMd5();
    }

    /**
     * Determines whether two Request HTTP header sets are non-varying
     *
     * @param string $vary Response vary header
     * @param array  $r1   HTTP header array
     * @param array  $r2   HTTP header array
     *
     * @return bool
     */
    private function requestsMatch($vary, $r1, $r2)
    {
        if ($vary) {
            foreach (explode(',', $vary) as $header) {
                $key = trim(strtolower($header));
                $v1 = isset($r1[$key]) ? $r1[$key] : null;
                $v2 = isset($r2[$key]) ? $r2[$key] : null;
                if ($v1 !== $v2) {
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * Creates an array of cacheable and normalized message headers
     *
     * @param MessageInterface $message
     *
     * @return array
     */
    private function persistHeaders(MessageInterface $message)
    {
        // Headers are excluded from the caching (see RFC 2616:13.5.1)
        static $noCache = array(
            'age' => true,
            'connection' => true,
            'keep-alive' => true,
            'proxy-authenticate' => true,
            'proxy-authorization' => true,
            'te' => true,
            'trailers' => true,
            'transfer-encoding' => true,
            'upgrade' => true,
            'set-cookie' => true,
            'set-cookie2' => true
        );

        // Clone the response to not destroy any necessary headers when caching
        $headers = $message->getHeaders()->getAll();
        $headers = array_diff_key($headers, $noCache);
        // Cast the headers to a string
        $headers = array_map(function ($h) { return (string) $h; }, $headers);

        return $headers;
    }
}
<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Default strategy used to determine of an HTTP request can be cached
 */
class DefaultCanCacheStrategy implements CanCacheStrategyInterface
{
    public function canCacheRequest(RequestInterface $request)
    {
        // Only GET and HEAD requests can be cached
        if ($request->getMethod() != RequestInterface::GET && $request->getMethod() != RequestInterface::HEAD) {
            return false;
        }

        // Never cache requests when using no-store
        if ($request->hasHeader('Cache-Control') && $request->getHeader('Cache-Control')->hasDirective('no-store')) {
            return false;
        }

        return true;
    }

    public function canCacheResponse(Response $response)
    {
        return $response->isSuccessful() && $response->canCache();
    }
}
<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\BadResponseException;

/**
 * Default revalidation strategy
 */
class DefaultRevalidation implements RevalidationInterface
{
    /** @var CacheStorageInterface Cache object storing cache data */
    protected $storage;

    /** @var CanCacheStrategyInterface */
    protected $canCache;

    /**
     * @param CacheStorageInterface     $cache    Cache storage
     * @param CanCacheStrategyInterface $canCache Determines if a message can be cached
     */
    public function __construct(CacheStorageInterface $cache, CanCacheStrategyInterface $canCache = null)
    {
        $this->storage = $cache;
        $this->canCache = $canCache ?: new DefaultCanCacheStrategy();
    }

    public function revalidate(RequestInterface $request, Response $response)
    {
        try {
            $revalidate = $this->createRevalidationRequest($request, $response);
            $validateResponse = $revalidate->send();
            if ($validateResponse->getStatusCode() == 200) {
                return $this->handle200Response($request, $validateResponse);
            } elseif ($validateResponse->getStatusCode() == 304) {
                return $this->handle304Response($request, $validateResponse, $response);
            }
        } catch (BadResponseException $e) {
            $this->handleBadResponse($e);
        }

        // Other exceptions encountered in the revalidation request are ignored
        // in hopes that sending a request to the origin server will fix it
        return false;
    }

    public function shouldRevalidate(RequestInterface $request, Response $response)
    {
        if ($request->getMethod() != RequestInterface::GET) {
            return false;
        }

        $reqCache = $request->getHeader('Cache-Control');
        $resCache = $response->getHeader('Cache-Control');

        $revalidate = $request->getHeader('Pragma') == 'no-cache' ||
            ($reqCache && ($reqCache->hasDirective('no-cache') || $reqCache->hasDirective('must-revalidate'))) ||
            ($resCache && ($resCache->hasDirective('no-cache') || $resCache->hasDirective('must-revalidate')));

        // Use the strong ETag validator if available and the response contains no Cache-Control directive
        if (!$revalidate && !$resCache && $response->hasHeader('ETag')) {
            $revalidate = true;
        }

        return $revalidate;
    }

    /**
     * Handles a bad response when attempting to revalidate
     *
     * @param BadResponseException $e Exception encountered
     *
     * @throws BadResponseException
     */
    protected function handleBadResponse(BadResponseException $e)
    {
        // 404 errors mean the resource no longer exists, so remove from
        // cache, and prevent an additional request by throwing the exception
        if ($e->getResponse()->getStatusCode() == 404) {
            $this->storage->delete($e->getRequest());
            throw $e;
        }
    }

    /**
     * Creates a request to use for revalidation
     *
     * @param RequestInterface $request  Request
     * @param Response         $response Response to revalidate
     *
     * @return RequestInterface returns a revalidation request
     */
    protected function createRevalidationRequest(RequestInterface $request, Response $response)
    {
        $revalidate = clone $request;
        $revalidate->removeHeader('Pragma')->removeHeader('Cache-Control');

        if ($response->getLastModified()) {
            $revalidate->setHeader('If-Modified-Since', $response->getLastModified());
        }

        if ($response->getEtag()) {
            $revalidate->setHeader('If-None-Match', $response->getEtag());
        }

        // Remove any cache plugins that might be on the request to prevent infinite recursive revalidations
        $dispatcher = $revalidate->getEventDispatcher();
        foreach ($dispatcher->getListeners() as $eventName => $listeners) {
            foreach ($listeners as $listener) {
                if (is_array($listener) && $listener[0] instanceof CachePlugin) {
                    $dispatcher->removeListener($eventName, $listener);
                }
            }
        }

        return $revalidate;
    }

    /**
     * Handles a 200 response response from revalidating. The server does not support validation, so use this response.
     *
     * @param RequestInterface $request          Request that was sent
     * @param Response         $validateResponse Response received
     *
     * @return bool Returns true if valid, false if invalid
     */
    protected function handle200Response(RequestInterface $request, Response $validateResponse)
    {
        $request->setResponse($validateResponse);
        if ($this->canCache->canCacheResponse($validateResponse)) {
            $this->storage->cache($request, $validateResponse);
        }

        return false;
    }

    /**
     * Handle a 304 response and ensure that it is still valid
     *
     * @param RequestInterface $request          Request that was sent
     * @param Response         $validateResponse Response received
     * @param Response         $response         Original cached response
     *
     * @return bool Returns true if valid, false if invalid
     */
    protected function handle304Response(RequestInterface $request, Response $validateResponse, Response $response)
    {
        static $replaceHeaders = array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified');

        // Make sure that this response has the same ETag
        if ($validateResponse->getEtag() != $response->getEtag()) {
            return false;
        }

        // Replace cached headers with any of these headers from the
        // origin server that might be more up to date
        $modified = false;
        foreach ($replaceHeaders as $name) {
            if ($validateResponse->hasHeader($name)) {
                $modified = true;
                $response->setHeader($name, $validateResponse->getHeader($name));
            }
        }

        // Store the updated response in cache
        if ($modified && $this->canCache->canCacheResponse($response)) {
            $this->storage->cache($request, $response);
        }

        return true;
    }
}
<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Never performs cache revalidation and just assumes the request is invalid
 */
class DenyRevalidation extends DefaultRevalidation
{
    public function __construct() {}

    public function revalidate(RequestInterface $request, Response $response)
    {
        return false;
    }
}
<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Cache revalidation interface
 */
interface RevalidationInterface
{
    /**
     * Performs a cache revalidation
     *
     * @param RequestInterface $request    Request to revalidate
     * @param Response         $response   Response that was received
     *
     * @return bool Returns true if the request can be cached
     */
    public function revalidate(RequestInterface $request, Response $response);

    /**
     * Returns true if the response should be revalidated
     *
     * @param RequestInterface $request  Request to check
     * @param Response         $response Response to check
     *
     * @return bool
     */
    public function shouldRevalidate(RequestInterface $request, Response $response);
}
<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Never performs cache revalidation and just assumes the request is still ok
 */
class SkipRevalidation extends DefaultRevalidation
{
    public function __construct() {}

    public function revalidate(RequestInterface $request, Response $response)
    {
        return true;
    }
}
<?php

namespace Guzzle\Plugin\Cookie;

use Guzzle\Common\ToArrayInterface;

/**
 * Set-Cookie object
 */
class Cookie implements ToArrayInterface
{
    /** @var array Cookie data */
    protected $data;

    /**
     * @var string ASCII codes not valid for for use in a cookie name
     *
     * Cookie names are defined as 'token', according to RFC 2616, Section 2.2
     * A valid token may contain any CHAR except CTLs (ASCII 0 - 31 or 127)
     * or any of the following separators
     */
    protected static $invalidCharString;

    /**
     * Gets an array of invalid cookie characters
     *
     * @return array
     */
    protected static function getInvalidCharacters()
    {
        if (!self::$invalidCharString) {
            self::$invalidCharString = implode('', array_map('chr', array_merge(
                range(0, 32),
                array(34, 40, 41, 44, 47),
                array(58, 59, 60, 61, 62, 63, 64, 91, 92, 93, 123, 125, 127)
            )));
        }

        return self::$invalidCharString;
    }

    /**
     * @param array $data Array of cookie data provided by a Cookie parser
     */
    public function __construct(array $data = array())
    {
        static $defaults = array(
            'name'        => '',
            'value'       => '',
            'domain'      => '',
            'path'        => '/',
            'expires'     => null,
            'max_age'     => 0,
            'comment'     => null,
            'comment_url' => null,
            'port'        => array(),
            'version'     => null,
            'secure'      => false,
            'discard'     => false,
            'http_only'   => false
        );

        $this->data = array_merge($defaults, $data);
        // Extract the expires value and turn it into a UNIX timestamp if needed
        if (!$this->getExpires() && $this->getMaxAge()) {
            // Calculate the expires date
            $this->setExpires(time() + (int) $this->getMaxAge());
        } elseif ($this->getExpires() && !is_numeric($this->getExpires())) {
            $this->setExpires(strtotime($this->getExpires()));
        }
    }

    /**
     * Get the cookie as an array
     *
     * @return array
     */
    public function toArray()
    {
        return $this->data;
    }

    /**
     * Get the cookie name
     *
     * @return string
     */
    public function getName()
    {
        return $this->data['name'];
    }

    /**
     * Set the cookie name
     *
     * @param string $name Cookie name
     *
     * @return Cookie
     */
    public function setName($name)
    {
        return $this->setData('name', $name);
    }

    /**
     * Get the cookie value
     *
     * @return string
     */
    public function getValue()
    {
        return $this->data['value'];
    }

    /**
     * Set the cookie value
     *
     * @param string $value Cookie value
     *
     * @return Cookie
     */
    public function setValue($value)
    {
        return $this->setData('value', $value);
    }

    /**
     * Get the domain
     *
     * @return string|null
     */
    public function getDomain()
    {
        return $this->data['domain'];
    }

    /**
     * Set the domain of the cookie
     *
     * @param string $domain
     *
     * @return Cookie
     */
    public function setDomain($domain)
    {
        return $this->setData('domain', $domain);
    }

    /**
     * Get the path
     *
     * @return string
     */
    public function getPath()
    {
        return $this->data['path'];
    }

    /**
     * Set the path of the cookie
     *
     * @param string $path Path of the cookie
     *
     * @return Cookie
     */
    public function setPath($path)
    {
        return $this->setData('path', $path);
    }

    /**
     * Maximum lifetime of the cookie in seconds
     *
     * @return int|null
     */
    public function getMaxAge()
    {
        return $this->data['max_age'];
    }

    /**
     * Set the max-age of the cookie
     *
     * @param int $maxAge Max age of the cookie in seconds
     *
     * @return Cookie
     */
    public function setMaxAge($maxAge)
    {
        return $this->setData('max_age', $maxAge);
    }

    /**
     * The UNIX timestamp when the cookie expires
     *
     * @return mixed
     */
    public function getExpires()
    {
        return $this->data['expires'];
    }

    /**
     * Set the unix timestamp for which the cookie will expire
     *
     * @param int $timestamp Unix timestamp
     *
     * @return Cookie
     */
    public function setExpires($timestamp)
    {
        return $this->setData('expires', $timestamp);
    }

    /**
     * Version of the cookie specification. RFC 2965 is 1
     *
     * @return mixed
     */
    public function getVersion()
    {
        return $this->data['version'];
    }

    /**
     * Set the cookie version
     *
     * @param string|int $version Version to set
     *
     * @return Cookie
     */
    public function setVersion($version)
    {
        return $this->setData('version', $version);
    }

    /**
     * Get whether or not this is a secure cookie
     *
     * @return null|bool
     */
    public function getSecure()
    {
        return $this->data['secure'];
    }

    /**
     * Set whether or not the cookie is secure
     *
     * @param bool $secure Set to true or false if secure
     *
     * @return Cookie
     */
    public function setSecure($secure)
    {
        return $this->setData('secure', (bool) $secure);
    }

    /**
     * Get whether or not this is a session cookie
     *
     * @return null|bool
     */
    public function getDiscard()
    {
        return $this->data['discard'];
    }

    /**
     * Set whether or not this is a session cookie
     *
     * @param bool $discard Set to true or false if this is a session cookie
     *
     * @return Cookie
     */
    public function setDiscard($discard)
    {
        return $this->setData('discard', $discard);
    }

    /**
     * Get the comment
     *
     * @return string|null
     */
    public function getComment()
    {
        return $this->data['comment'];
    }

    /**
     * Set the comment of the cookie
     *
     * @param string $comment Cookie comment
     *
     * @return Cookie
     */
    public function setComment($comment)
    {
        return $this->setData('comment', $comment);
    }

    /**
     * Get the comment URL of the cookie
     *
     * @return string|null
     */
    public function getCommentUrl()
    {
        return $this->data['comment_url'];
    }

    /**
     * Set the comment URL of the cookie
     *
     * @param string $commentUrl Cookie comment URL for more information
     *
     * @return Cookie
     */
    public function setCommentUrl($commentUrl)
    {
        return $this->setData('comment_url', $commentUrl);
    }

    /**
     * Get an array of acceptable ports this cookie can be used with
     *
     * @return array
     */
    public function getPorts()
    {
        return $this->data['port'];
    }

    /**
     * Set a list of acceptable ports this cookie can be used with
     *
     * @param array $ports Array of acceptable ports
     *
     * @return Cookie
     */
    public function setPorts(array $ports)
    {
        return $this->setData('port', $ports);
    }

    /**
     * Get whether or not this is an HTTP only cookie
     *
     * @return bool
     */
    public function getHttpOnly()
    {
        return $this->data['http_only'];
    }

    /**
     * Set whether or not this is an HTTP only cookie
     *
     * @param bool $httpOnly Set to true or false if this is HTTP only
     *
     * @return Cookie
     */
    public function setHttpOnly($httpOnly)
    {
        return $this->setData('http_only', $httpOnly);
    }

    /**
     * Get an array of extra cookie data
     *
     * @return array
     */
    public function getAttributes()
    {
        return $this->data['data'];
    }

    /**
     * Get a specific data point from the extra cookie data
     *
     * @param string $name Name of the data point to retrieve
     *
     * @return null|string
     */
    public function getAttribute($name)
    {
        return array_key_exists($name, $this->data['data']) ? $this->data['data'][$name] : null;
    }

    /**
     * Set a cookie data attribute
     *
     * @param string $name  Name of the attribute to set
     * @param string $value Value to set
     *
     * @return Cookie
     */
    public function setAttribute($name, $value)
    {
        $this->data['data'][$name] = $value;

        return $this;
    }

    /**
     * Check if the cookie matches a path value
     *
     * @param string $path Path to check against
     *
     * @return bool
     */
    public function matchesPath($path)
    {
        // RFC6265 http://tools.ietf.org/search/rfc6265#section-5.1.4
        // A request-path path-matches a given cookie-path if at least one of
        // the following conditions holds:

        // o  The cookie-path and the request-path are identical.
        if ($path == $this->getPath()) {
            return true;
        }

        $pos = stripos($path, $this->getPath());
        if ($pos === 0) {
            // o  The cookie-path is a prefix of the request-path, and the last
            // character of the cookie-path is %x2F ("/").
            if (substr($this->getPath(), -1, 1) === "/") {
                return true;
            }

            // o  The cookie-path is a prefix of the request-path, and the first
            // character of the request-path that is not included in the cookie-
            // path is a %x2F ("/") character.
            if (substr($path, strlen($this->getPath()), 1) === "/") {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if the cookie matches a domain value
     *
     * @param string $domain Domain to check against
     *
     * @return bool
     */
    public function matchesDomain($domain)
    {
        // Remove the leading '.' as per spec in RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.2.3
        $cookieDomain = ltrim($this->getDomain(), '.');

        // Domain not set or exact match.
        if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) {
            return true;
        }

        // Matching the subdomain according to RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.1.3
        if (filter_var($domain, FILTER_VALIDATE_IP)) {
            return false;
        }

        return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/i', $domain);
    }

    /**
     * Check if the cookie is compatible with a specific port
     *
     * @param int $port Port to check
     *
     * @return bool
     */
    public function matchesPort($port)
    {
        return count($this->getPorts()) == 0 || in_array($port, $this->getPorts());
    }

    /**
     * Check if the cookie is expired
     *
     * @return bool
     */
    public function isExpired()
    {
        return $this->getExpires() && time() > $this->getExpires();
    }

    /**
     * Check if the cookie is valid according to RFC 6265
     *
     * @return bool|string Returns true if valid or an error message if invalid
     */
    public function validate()
    {
        // Names must not be empty, but can be 0
        $name = $this->getName();
        if (empty($name) && !is_numeric($name)) {
            return 'The cookie name must not be empty';
        }

        // Check if any of the invalid characters are present in the cookie name
        if (strpbrk($name, self::getInvalidCharacters()) !== false) {
            return 'The cookie name must not contain invalid characters: ' . $name;
        }

        // Value must not be empty, but can be 0
        $value = $this->getValue();
        if (empty($value) && !is_numeric($value)) {
            return 'The cookie value must not be empty';
        }

        // Domains must not be empty, but can be 0
        // A "0" is not a valid internet domain, but may be used as server name in a private network
        $domain = $this->getDomain();
        if (empty($domain) && !is_numeric($domain)) {
            return 'The cookie domain must not be empty';
        }

        return true;
    }

    /**
     * Set a value and return the cookie object
     *
     * @param string $key   Key to set
     * @param string $value Value to set
     *
     * @return Cookie
     */
    private function setData($key, $value)
    {
        $this->data[$key] = $value;

        return $this;
    }
}
<?php

namespace Guzzle\Plugin\Cookie\CookieJar;

use Guzzle\Plugin\Cookie\Cookie;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Parser\ParserRegistry;
use Guzzle\Plugin\Cookie\Exception\InvalidCookieException;

/**
 * Cookie cookieJar that stores cookies an an array
 */
class ArrayCookieJar implements CookieJarInterface, \Serializable
{
    /** @var array Loaded cookie data */
    protected $cookies = array();

    /** @var bool Whether or not strict mode is enabled. When enabled, exceptions will be thrown for invalid cookies */
    protected $strictMode;

    /**
     * @param bool $strictMode Set to true to throw exceptions when invalid cookies are added to the cookie jar
     */
    public function __construct($strictMode = false)
    {
        $this->strictMode = $strictMode;
    }

    /**
     * Enable or disable strict mode on the cookie jar
     *
     * @param bool $strictMode Set to true to throw exceptions when invalid cookies are added. False to ignore them.
     *
     * @return self
     */
    public function setStrictMode($strictMode)
    {
        $this->strictMode = $strictMode;
    }

    public function remove($domain = null, $path = null, $name = null)
    {
        $cookies = $this->all($domain, $path, $name, false, false);
        $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($cookies) {
            return !in_array($cookie, $cookies, true);
        });

        return $this;
    }

    public function removeTemporary()
    {
        $this->cookies = array_filter($this->cookies, function (Cookie $cookie) {
            return !$cookie->getDiscard() && $cookie->getExpires();
        });

        return $this;
    }

    public function removeExpired()
    {
        $currentTime = time();
        $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($currentTime) {
            return !$cookie->getExpires() || $currentTime < $cookie->getExpires();
        });

        return $this;
    }

    public function all($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true)
    {
        return array_values(array_filter($this->cookies, function (Cookie $cookie) use (
            $domain,
            $path,
            $name,
            $skipDiscardable,
            $skipExpired
        ) {
            return false === (($name && $cookie->getName() != $name) ||
                ($skipExpired && $cookie->isExpired()) ||
                ($skipDiscardable && ($cookie->getDiscard() || !$cookie->getExpires())) ||
                ($path && !$cookie->matchesPath($path)) ||
                ($domain && !$cookie->matchesDomain($domain)));
        }));
    }

    public function add(Cookie $cookie)
    {
        // Only allow cookies with set and valid domain, name, value
        $result = $cookie->validate();
        if ($result !== true) {
            if ($this->strictMode) {
                throw new InvalidCookieException($result);
            } else {
                $this->removeCookieIfEmpty($cookie);
                return false;
            }
        }

        // Resolve conflicts with previously set cookies
        foreach ($this->cookies as $i => $c) {

            // Two cookies are identical, when their path, domain, port and name are identical
            if ($c->getPath() != $cookie->getPath() ||
                $c->getDomain() != $cookie->getDomain() ||
                $c->getPorts() != $cookie->getPorts() ||
                $c->getName() != $cookie->getName()
            ) {
                continue;
            }

            // The previously set cookie is a discard cookie and this one is not so allow the new cookie to be set
            if (!$cookie->getDiscard() && $c->getDiscard()) {
                unset($this->cookies[$i]);
                continue;
            }

            // If the new cookie's expiration is further into the future, then replace the old cookie
            if ($cookie->getExpires() > $c->getExpires()) {
                unset($this->cookies[$i]);
                continue;
            }

            // If the value has changed, we better change it
            if ($cookie->getValue() !== $c->getValue()) {
                unset($this->cookies[$i]);
                continue;
            }

            // The cookie exists, so no need to continue
            return false;
        }

        $this->cookies[] = $cookie;

        return true;
    }

    /**
     * Serializes the cookie cookieJar
     *
     * @return string
     */
    public function serialize()
    {
        // Only serialize long term cookies and unexpired cookies
        return json_encode(array_map(function (Cookie $cookie) {
            return $cookie->toArray();
        }, $this->all(null, null, null, true, true)));
    }

    /**
     * Unserializes the cookie cookieJar
     */
    public function unserialize($data)
    {
        $data = json_decode($data, true);
        if (empty($data)) {
            $this->cookies = array();
        } else {
            $this->cookies = array_map(function (array $cookie) {
                return new Cookie($cookie);
            }, $data);
        }
    }

    /**
     * Returns the total number of stored cookies
     *
     * @return int
     */
    public function count()
    {
        return count($this->cookies);
    }

    /**
     * Returns an iterator
     *
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->cookies);
    }

    public function addCookiesFromResponse(Response $response, RequestInterface $request = null)
    {
        if ($cookieHeader = $response->getHeader('Set-Cookie')) {
            $parser = ParserRegistry::getInstance()->getParser('cookie');
            foreach ($cookieHeader as $cookie) {
                if ($parsed = $request
                    ? $parser->parseCookie($cookie, $request->getHost(), $request->getPath())
                    : $parser->parseCookie($cookie)
                ) {
                    // Break up cookie v2 into multiple cookies
                    foreach ($parsed['cookies'] as $key => $value) {
                        $row = $parsed;
                        $row['name'] = $key;
                        $row['value'] = $value;
                        unset($row['cookies']);
                        $this->add(new Cookie($row));
                    }
                }
            }
        }
    }

    public function getMatchingCookies(RequestInterface $request)
    {
        // Find cookies that match this request
        $cookies = $this->all($request->getHost(), $request->getPath());
        // Remove ineligible cookies
        foreach ($cookies as $index => $cookie) {
            if (!$cookie->matchesPort($request->getPort()) || ($cookie->getSecure() && $request->getScheme() != 'https')) {
                unset($cookies[$index]);
            }
        };

        return $cookies;
    }

    /**
     * If a cookie already exists and the server asks to set it again with a null value, the
     * cookie must be deleted.
     *
     * @param \Guzzle\Plugin\Cookie\Cookie $cookie
     */
    private function removeCookieIfEmpty(Cookie $cookie)
    {
        $cookieValue = $cookie->getValue();
        if ($cookieValue === null || $cookieValue === '') {
            $this->remove($cookie->getDomain(), $cookie->getPath(), $cookie->getName());
        }
    }
}
<?php

namespace Guzzle\Plugin\Cookie\CookieJar;

use Guzzle\Plugin\Cookie\Cookie;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Interface for persisting cookies
 */
interface CookieJarInterface extends \Countable, \IteratorAggregate
{
    /**
     * Remove cookies currently held in the Cookie cookieJar.
     *
     * Invoking this method without arguments will empty the whole Cookie cookieJar.  If given a $domain argument only
     * cookies belonging to that domain will be removed. If given a $domain and $path argument, cookies belonging to
     * the specified path within that domain are removed. If given all three arguments, then the cookie with the
     * specified name, path and domain is removed.
     *
     * @param string $domain Set to clear only cookies matching a domain
     * @param string $path   Set to clear only cookies matching a domain and path
     * @param string $name   Set to clear only cookies matching a domain, path, and name
     *
     * @return CookieJarInterface
     */
    public function remove($domain = null, $path = null, $name = null);

    /**
     * Discard all temporary cookies.
     *
     * Scans for all cookies in the cookieJar with either no expire field or a true discard flag. To be called when the
     * user agent shuts down according to RFC 2965.
     *
     * @return CookieJarInterface
     */
    public function removeTemporary();

    /**
     * Delete any expired cookies
     *
     * @return CookieJarInterface
     */
    public function removeExpired();

    /**
     * Add a cookie to the cookie cookieJar
     *
     * @param Cookie $cookie Cookie to add
     *
     * @return bool Returns true on success or false on failure
     */
    public function add(Cookie $cookie);

    /**
     * Add cookies from a {@see Guzzle\Http\Message\Response} object
     *
     * @param Response         $response Response object
     * @param RequestInterface $request  Request that received the response
     */
    public function addCookiesFromResponse(Response $response, RequestInterface $request = null);

    /**
     * Get cookies matching a request object
     *
     * @param RequestInterface $request Request object to match
     *
     * @return array
     */
    public function getMatchingCookies(RequestInterface $request);

    /**
     * Get all of the matching cookies
     *
     * @param string $domain          Domain of the cookie
     * @param string $path            Path of the cookie
     * @param string $name            Name of the cookie
     * @param bool   $skipDiscardable Set to TRUE to skip cookies with the Discard attribute.
     * @param bool   $skipExpired     Set to FALSE to include expired
     *
     * @return array Returns an array of Cookie objects
     */
    public function all($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true);
}
<?php

namespace Guzzle\Plugin\Cookie\CookieJar;

use Guzzle\Common\Exception\RuntimeException;

/**
 * Persists non-session cookies using a JSON formatted file
 */
class FileCookieJar extends ArrayCookieJar
{
    /** @var string filename */
    protected $filename;

    /**
     * Create a new FileCookieJar object
     *
     * @param string $cookieFile File to store the cookie data
     *
     * @throws RuntimeException if the file cannot be found or created
     */
    public function __construct($cookieFile)
    {
        $this->filename = $cookieFile;
        $this->load();
    }

    /**
     * Saves the file when shutting down
     */
    public function __destruct()
    {
        $this->persist();
    }

    /**
     * Save the contents of the data array to the file
     *
     * @throws RuntimeException if the file cannot be found or created
     */
    protected function persist()
    {
        if (false === file_put_contents($this->filename, $this->serialize())) {
            // @codeCoverageIgnoreStart
            throw new RuntimeException('Unable to open file ' . $this->filename);
            // @codeCoverageIgnoreEnd
        }
    }

    /**
     * Load the contents of the json formatted file into the data array and discard any unsaved state
     */
    protected function load()
    {
        $json = file_get_contents($this->filename);
        if (false === $json) {
            // @codeCoverageIgnoreStart
            throw new RuntimeException('Unable to open file ' . $this->filename);
            // @codeCoverageIgnoreEnd
        }

        $this->unserialize($json);
        $this->cookies = $this->cookies ?: array();
    }
}
<?php

namespace Guzzle\Plugin\Cookie;

use Guzzle\Common\Event;
use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar;
use Guzzle\Plugin\Cookie\CookieJar\CookieJarInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Adds, extracts, and persists cookies between HTTP requests
 */
class CookiePlugin implements EventSubscriberInterface
{
    /** @var CookieJarInterface Cookie cookieJar used to hold cookies */
    protected $cookieJar;

    /**
     * @param CookieJarInterface $cookieJar Cookie jar used to hold cookies. Creates an ArrayCookieJar by default.
     */
    public function __construct(CookieJarInterface $cookieJar = null)
    {
        $this->cookieJar = $cookieJar ?: new ArrayCookieJar();
    }

    public static function getSubscribedEvents()
    {
        return array(
            'request.before_send' => array('onRequestBeforeSend', 125),
            'request.sent'        => array('onRequestSent', 125)
        );
    }

    /**
     * Get the cookie cookieJar
     *
     * @return CookieJarInterface
     */
    public function getCookieJar()
    {
        return $this->cookieJar;
    }

    /**
     * Add cookies before a request is sent
     *
     * @param Event $event
     */
    public function onRequestBeforeSend(Event $event)
    {
        $request = $event['request'];
        if (!$request->getParams()->get('cookies.disable')) {
            $request->removeHeader('Cookie');
            // Find cookies that match this request
            foreach ($this->cookieJar->getMatchingCookies($request) as $cookie) {
                $request->addCookie($cookie->getName(), $cookie->getValue());
            }
        }
    }

    /**
     * Extract cookies from a sent request
     *
     * @param Event $event
     */
    public function onRequestSent(Event $event)
    {
        $this->cookieJar->addCookiesFromResponse($event['response'], $event['request']);
    }
}
<?php

namespace Guzzle\Plugin\Cookie\Exception;

use Guzzle\Common\Exception\InvalidArgumentException;

class InvalidCookieException extends InvalidArgumentException {}
<?php

namespace Guzzle\Plugin\CurlAuth;

use Guzzle\Common\Event;
use Guzzle\Common\Version;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Adds specified curl auth to all requests sent from a client. Defaults to CURLAUTH_BASIC if none supplied.
 * @deprecated Use $client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');
 */
class CurlAuthPlugin implements EventSubscriberInterface
{
    private $username;
    private $password;
    private $scheme;

    /**
     * @param string $username HTTP basic auth username
     * @param string $password Password
     * @param int    $scheme   Curl auth scheme
     */
    public function __construct($username, $password, $scheme=CURLAUTH_BASIC)
    {
        Version::warn(__CLASS__ . " is deprecated. Use \$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');");
        $this->username = $username;
        $this->password = $password;
        $this->scheme = $scheme;
    }

    public static function getSubscribedEvents()
    {
        return array('client.create_request' => array('onRequestCreate', 255));
    }

    /**
     * Add basic auth
     *
     * @param Event $event
     */
    public function onRequestCreate(Event $event)
    {
        $event['request']->setAuth($this->username, $this->password, $this->scheme);
    }
}
<?php

namespace Guzzle\Plugin\ErrorResponse;

use Guzzle\Service\Command\CommandInterface;
use Guzzle\Http\Message\Response;

/**
 * Interface used to create an exception from an error response
 */
interface ErrorResponseExceptionInterface
{
    /**
     * Create an exception for a command based on a command and an error response definition
     *
     * @param CommandInterface $command  Command that was sent
     * @param Response         $response The error response
     *
     * @return self
     */
    public static function fromCommand(CommandInterface $command, Response $response);
}
<?php

namespace Guzzle\Plugin\ErrorResponse;

use Guzzle\Common\Event;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Operation;
use Guzzle\Plugin\ErrorResponse\Exception\ErrorResponseException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Converts generic Guzzle response exceptions into errorResponse exceptions
 */
class ErrorResponsePlugin implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array('command.before_send' => array('onCommandBeforeSend', -1));
    }

    /**
     * Adds a listener to requests before they sent from a command
     *
     * @param Event $event Event emitted
     */
    public function onCommandBeforeSend(Event $event)
    {
        $command = $event['command'];
        if ($operation = $command->getOperation()) {
            if ($operation->getErrorResponses()) {
                $request = $command->getRequest();
                $request->getEventDispatcher()
                    ->addListener('request.complete', $this->getErrorClosure($request, $command, $operation));
            }
        }
    }

    /**
     * @param RequestInterface $request   Request that received an error
     * @param CommandInterface $command   Command that created the request
     * @param Operation        $operation Operation that defines the request and errors
     *
     * @return \Closure Returns a closure
     * @throws ErrorResponseException
     */
    protected function getErrorClosure(RequestInterface $request, CommandInterface $command, Operation $operation)
    {
        return function (Event $event) use ($request, $command, $operation) {
            $response = $event['response'];
            foreach ($operation->getErrorResponses() as $error) {
                if (!isset($error['class'])) {
                    continue;
                }
                if (isset($error['code']) && $response->getStatusCode() != $error['code']) {
                    continue;
                }
                if (isset($error['reason']) && $response->getReasonPhrase() != $error['reason']) {
                    continue;
                }
                $className = $error['class'];
                $errorClassInterface = __NAMESPACE__ . '\\ErrorResponseExceptionInterface';
                if (!class_exists($className)) {
                    throw new ErrorResponseException("{$className} does not exist");
                } elseif (!(in_array($errorClassInterface, class_implements($className)))) {
                    throw new ErrorResponseException("{$className} must implement {$errorClassInterface}");
                }
                throw $className::fromCommand($command, $response);
            }
        };
    }
}
<?php

namespace Guzzle\Plugin\ErrorResponse\Exception;

use Guzzle\Common\Exception\RuntimeException;

class ErrorResponseException extends RuntimeException {}
<?php

namespace Guzzle\Plugin\History;

use Guzzle\Common\Event;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Maintains a list of requests and responses sent using a request or client
 */
class HistoryPlugin implements EventSubscriberInterface, \IteratorAggregate, \Countable
{
    /** @var int The maximum number of requests to maintain in the history */
    protected $limit = 10;

    /** @var array Requests and responses that have passed through the plugin */
    protected $transactions = array();

    public static function getSubscribedEvents()
    {
        return array('request.sent' => array('onRequestSent', 9999));
    }

    /**
     * Convert to a string that contains all request and response headers
     *
     * @return string
     */
    public function __toString()
    {
        $lines = array();
        foreach ($this->transactions as $entry) {
            $response = isset($entry['response']) ? $entry['response'] : '';
            $lines[] = '> ' . trim($entry['request']) . "\n\n< " . trim($response) . "\n";
        }

        return implode("\n", $lines);
    }

    /**
     * Add a request to the history
     *
     * @param RequestInterface $request  Request to add
     * @param Response         $response Response of the request
     *
     * @return HistoryPlugin
     */
    public function add(RequestInterface $request, Response $response = null)
    {
        if (!$response && $request->getResponse()) {
            $response = $request->getResponse();
        }

        $this->transactions[] = array('request' => $request, 'response' => $response);
        if (count($this->transactions) > $this->getlimit()) {
            array_shift($this->transactions);
        }

        return $this;
    }

    /**
     * Set the max number of requests to store
     *
     * @param int $limit Limit
     *
     * @return HistoryPlugin
     */
    public function setLimit($limit)
    {
        $this->limit = (int) $limit;

        return $this;
    }

    /**
     * Get the request limit
     *
     * @return int
     */
    public function getLimit()
    {
        return $this->limit;
    }

    /**
     * Get all of the raw transactions in the form of an array of associative arrays containing
     * 'request' and 'response' keys.
     *
     * @return array
     */
    public function getAll()
    {
        return $this->transactions;
    }

    /**
     * Get the requests in the history
     *
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        // Return an iterator just like the old iteration of the HistoryPlugin for BC compatibility (use getAll())
        return new \ArrayIterator(array_map(function ($entry) {
            $entry['request']->getParams()->set('actual_response', $entry['response']);
            return $entry['request'];
        }, $this->transactions));
    }

    /**
     * Get the number of requests in the history
     *
     * @return int
     */
    public function count()
    {
        return count($this->transactions);
    }

    /**
     * Get the last request sent
     *
     * @return RequestInterface
     */
    public function getLastRequest()
    {
        $last = end($this->transactions);

        return $last['request'];
    }

    /**
     * Get the last response in the history
     *
     * @return Response|null
     */
    public function getLastResponse()
    {
        $last = end($this->transactions);

        return isset($last['response']) ? $last['response'] : null;
    }

    /**
     * Clears the history
     *
     * @return HistoryPlugin
     */
    public function clear()
    {
        $this->transactions = array();

        return $this;
    }

    public function onRequestSent(Event $event)
    {
        $this->add($event['request'], $event['response']);
    }
}
<?php

namespace Guzzle\Plugin\Log;

use Guzzle\Common\Event;
use Guzzle\Log\LogAdapterInterface;
use Guzzle\Log\MessageFormatter;
use Guzzle\Log\ClosureLogAdapter;
use Guzzle\Http\EntityBody;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Plugin class that will add request and response logging to an HTTP request.
 *
 * The log plugin uses a message formatter that allows custom messages via template variable substitution.
 *
 * @see MessageLogger for a list of available log template variable substitutions
 */
class LogPlugin implements EventSubscriberInterface
{
    /** @var LogAdapterInterface Adapter responsible for writing log data */
    protected $logAdapter;

    /** @var MessageFormatter Formatter used to format messages before logging */
    protected $formatter;

    /** @var bool Whether or not to wire request and response bodies */
    protected $wireBodies;

    /**
     * @param LogAdapterInterface     $logAdapter Adapter object used to log message
     * @param string|MessageFormatter $formatter  Formatter used to format log messages or the formatter template
     * @param bool                    $wireBodies Set to true to track request and response bodies using a temporary
     *                                            buffer if the bodies are not repeatable.
     */
    public function __construct(
        LogAdapterInterface $logAdapter,
        $formatter = null,
        $wireBodies = false
    ) {
        $this->logAdapter = $logAdapter;
        $this->formatter = $formatter instanceof MessageFormatter ? $formatter : new MessageFormatter($formatter);
        $this->wireBodies = $wireBodies;
    }

    /**
     * Get a log plugin that outputs full request, response, and curl error information to stderr
     *
     * @param bool     $wireBodies Set to false to disable request/response body output when they use are not repeatable
     * @param resource $stream     Stream to write to when logging. Defaults to STDERR when it is available
     *
     * @return self
     */
    public static function getDebugPlugin($wireBodies = true, $stream = null)
    {
        if ($stream === null) {
            if (defined('STDERR')) {
                $stream = STDERR;
            } else {
                $stream = fopen('php://output', 'w');
            }
        }

        return new self(new ClosureLogAdapter(function ($m) use ($stream) {
            fwrite($stream, $m . PHP_EOL);
        }), "# Request:\n{request}\n\n# Response:\n{response}\n\n# Errors: {curl_code} {curl_error}", $wireBodies);
    }

    public static function getSubscribedEvents()
    {
        return array(
            'curl.callback.write' => array('onCurlWrite', 255),
            'curl.callback.read'  => array('onCurlRead', 255),
            'request.before_send' => array('onRequestBeforeSend', 255),
            'request.sent'        => array('onRequestSent', 255)
        );
    }

    /**
     * Event triggered when curl data is read from a request
     *
     * @param Event $event
     */
    public function onCurlRead(Event $event)
    {
        // Stream the request body to the log if the body is not repeatable
        if ($wire = $event['request']->getParams()->get('request_wire')) {
            $wire->write($event['read']);
        }
    }

    /**
     * Event triggered when curl data is written to a response
     *
     * @param Event $event
     */
    public function onCurlWrite(Event $event)
    {
        // Stream the response body to the log if the body is not repeatable
        if ($wire = $event['request']->getParams()->get('response_wire')) {
            $wire->write($event['write']);
        }
    }

    /**
     * Called before a request is sent
     *
     * @param Event $event
     */
    public function onRequestBeforeSend(Event $event)
    {
        if ($this->wireBodies) {
            $request = $event['request'];
            // Ensure that curl IO events are emitted
            $request->getCurlOptions()->set('emit_io', true);
            // We need to make special handling for content wiring and non-repeatable streams.
            if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()
                && (!$request->getBody()->isSeekable() || !$request->getBody()->isReadable())
            ) {
                // The body of the request cannot be recalled so logging the body will require us to buffer it
                $request->getParams()->set('request_wire', EntityBody::factory());
            }
            if (!$request->getResponseBody()->isRepeatable()) {
                // The body of the response cannot be recalled so logging the body will require us to buffer it
                $request->getParams()->set('response_wire', EntityBody::factory());
            }
        }
    }

    /**
     * Triggers the actual log write when a request completes
     *
     * @param Event $event
     */
    public function onRequestSent(Event $event)
    {
        $request = $event['request'];
        $response = $event['response'];
        $handle = $event['handle'];

        if ($wire = $request->getParams()->get('request_wire')) {
            $request = clone $request;
            $request->setBody($wire);
        }

        if ($wire = $request->getParams()->get('response_wire')) {
            $response = clone $response;
            $response->setBody($wire);
        }

        // Send the log message to the adapter, adding a category and host
        $priority = $response && $response->isError() ? LOG_ERR : LOG_DEBUG;
        $message = $this->formatter->format($request, $response, $handle);
        $this->logAdapter->log($message, $priority, array(
            'request'  => $request,
            'response' => $response,
            'handle'   => $handle
        ));
    }
}
<?php

namespace Guzzle\Plugin\Md5;

use Guzzle\Common\Event;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Listener used to add a ContentMD5 header to the body of a command and adds ContentMD5 validation if the
 * ValidateMD5 option is not set to false on a command
 */
class CommandContentMd5Plugin  implements EventSubscriberInterface
{
    /** @var string Parameter used to check if the ContentMD5 value is being added */
    protected $contentMd5Param;

    /** @var string Parameter used to check if validation should occur on the response */
    protected $validateMd5Param;

    /**
     * @param string $contentMd5Param  Parameter used to check if the ContentMD5 value is being added
     * @param string $validateMd5Param Parameter used to check if validation should occur on the response
     */
    public function __construct($contentMd5Param = 'ContentMD5', $validateMd5Param = 'ValidateMD5')
    {
        $this->contentMd5Param = $contentMd5Param;
        $this->validateMd5Param = $validateMd5Param;
    }

    public static function getSubscribedEvents()
    {
        return array('command.before_send' => array('onCommandBeforeSend', -255));
    }

    public function onCommandBeforeSend(Event $event)
    {
        $command = $event['command'];
        $request = $command->getRequest();

        // Only add an MD5 is there is a MD5 option on the operation and it has a payload
        if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()
            && $command->getOperation()->hasParam($this->contentMd5Param)) {
            // Check if an MD5 checksum value should be passed along to the request
            if ($command[$this->contentMd5Param] === true) {
                if (false !== ($md5 = $request->getBody()->getContentMd5(true, true))) {
                    $request->setHeader('Content-MD5', $md5);
                }
            }
        }

        // Check if MD5 validation should be used with the response
        if ($command[$this->validateMd5Param] === true) {
            $request->addSubscriber(new Md5ValidatorPlugin(true, false));
        }
    }
}
<?php

namespace Guzzle\Plugin\Md5;

use Guzzle\Common\Event;
use Guzzle\Common\Exception\UnexpectedValueException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Ensures that an the MD5 hash of an entity body matches the Content-MD5
 * header (if set) of an HTTP response.  An exception is thrown if the
 * calculated MD5 does not match the expected MD5.
 */
class Md5ValidatorPlugin implements EventSubscriberInterface
{
    /** @var int Maximum Content-Length in bytes to validate */
    protected $contentLengthCutoff;

    /** @var bool Whether or not to compare when a Content-Encoding is present */
    protected $contentEncoded;

    /**
     * @param bool     $contentEncoded      Calculating the MD5 hash of an entity body where a Content-Encoding was
     *                                      applied is a more expensive comparison because the entity body will need to
     *                                      be compressed in order to get the correct hash.  Set to FALSE to not
     *                                      validate the MD5 hash of an entity body with an applied Content-Encoding.
     * @param bool|int $contentLengthCutoff Maximum Content-Length (bytes) in which a MD5 hash will be validated. Any
     *                                      response with a Content-Length greater than this value will not be validated
     *                                      because it will be deemed too memory intensive.
     */
    public function __construct($contentEncoded = true, $contentLengthCutoff = false)
    {
        $this->contentLengthCutoff = $contentLengthCutoff;
        $this->contentEncoded = $contentEncoded;
    }

    public static function getSubscribedEvents()
    {
        return array('request.complete' => array('onRequestComplete', 255));
    }

    /**
     * {@inheritdoc}
     * @throws UnexpectedValueException
     */
    public function onRequestComplete(Event $event)
    {
        $response = $event['response'];

        if (!$contentMd5 = $response->getContentMd5()) {
            return;
        }

        $contentEncoding = $response->getContentEncoding();
        if ($contentEncoding && !$this->contentEncoded) {
            return false;
        }

        // Make sure that the size of the request is under the cutoff size
        if ($this->contentLengthCutoff) {
            $size = $response->getContentLength() ?: $response->getBody()->getSize();
            if (!$size || $size > $this->contentLengthCutoff) {
                return;
            }
        }

        if (!$contentEncoding) {
            $hash = $response->getBody()->getContentMd5();
        } elseif ($contentEncoding == 'gzip') {
            $response->getBody()->compress('zlib.deflate');
            $hash = $response->getBody()->getContentMd5();
            $response->getBody()->uncompress();
        } elseif ($contentEncoding == 'compress') {
            $response->getBody()->compress('bzip2.compress');
            $hash = $response->getBody()->getContentMd5();
            $response->getBody()->uncompress();
        } else {
            return;
        }

        if ($contentMd5 !== $hash) {
            throw new UnexpectedValueException(
                "The response entity body may have been modified over the wire.  The Content-MD5 "
                . "received ({$contentMd5}) did not match the calculated MD5 hash ({$hash})."
            );
        }
    }
}
<?php

namespace Guzzle\Plugin\Mock;

use Guzzle\Common\Event;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Http\Exception\CurlException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\Response;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Queues mock responses or exceptions and delivers mock responses or exceptions in a fifo order.
 */
class MockPlugin extends AbstractHasDispatcher implements EventSubscriberInterface, \Countable
{
    /** @var array Array of mock responses / exceptions */
    protected $queue = array();

    /** @var bool Whether or not to remove the plugin when the queue is empty */
    protected $temporary = false;

    /** @var array Array of requests that were mocked */
    protected $received = array();

    /** @var bool Whether or not to consume an entity body when a mock response is served */
    protected $readBodies;

    /**
     * @param array $items      Array of responses or exceptions to queue
     * @param bool  $temporary  Set to TRUE to remove the plugin when the queue is empty
     * @param bool  $readBodies Set to TRUE to consume the entity body when a mock is served
     */
    public function __construct(array $items = null, $temporary = false, $readBodies = false)
    {
        $this->readBodies = $readBodies;
        $this->temporary = $temporary;
        if ($items) {
            foreach ($items as $item) {
                if ($item instanceof \Exception) {
                    $this->addException($item);
                } else {
                    $this->addResponse($item);
                }
            }
        }
    }

    public static function getSubscribedEvents()
    {
        // Use a number lower than the CachePlugin
        return array('request.before_send' => array('onRequestBeforeSend', -999));
    }

    public static function getAllEvents()
    {
        return array('mock.request');
    }

    /**
     * Get a mock response from a file
     *
     * @param string $path File to retrieve a mock response from
     *
     * @return Response
     * @throws InvalidArgumentException if the file is not found
     */
    public static function getMockFile($path)
    {
        if (!file_exists($path)) {
            throw new InvalidArgumentException('Unable to open mock file: ' . $path);
        }

        return Response::fromMessage(file_get_contents($path));
    }

    /**
     * Set whether or not to consume the entity body of a request when a mock
     * response is used
     *
     * @param bool $readBodies Set to true to read and consume entity bodies
     *
     * @return self
     */
    public function readBodies($readBodies)
    {
        $this->readBodies = $readBodies;

        return $this;
    }

    /**
     * Returns the number of remaining mock responses
     *
     * @return int
     */
    public function count()
    {
        return count($this->queue);
    }

    /**
     * Add a response to the end of the queue
     *
     * @param string|Response $response Response object or path to response file
     *
     * @return MockPlugin
     * @throws InvalidArgumentException if a string or Response is not passed
     */
    public function addResponse($response)
    {
        if (!($response instanceof Response)) {
            if (!is_string($response)) {
                throw new InvalidArgumentException('Invalid response');
            }
            $response = self::getMockFile($response);
        }

        $this->queue[] = $response;

        return $this;
    }

    /**
     * Add an exception to the end of the queue
     *
     * @param CurlException $e Exception to throw when the request is executed
     *
     * @return MockPlugin
     */
    public function addException(CurlException $e)
    {
        $this->queue[] = $e;

        return $this;
    }

    /**
     * Clear the queue
     *
     * @return MockPlugin
     */
    public function clearQueue()
    {
        $this->queue = array();

        return $this;
    }

    /**
     * Returns an array of mock responses remaining in the queue
     *
     * @return array
     */
    public function getQueue()
    {
        return $this->queue;
    }

    /**
     * Check if this is a temporary plugin
     *
     * @return bool
     */
    public function isTemporary()
    {
        return $this->temporary;
    }

    /**
     * Get a response from the front of the list and add it to a request
     *
     * @param RequestInterface $request Request to mock
     *
     * @return self
     * @throws CurlException When request.send is called and an exception is queued
     */
    public function dequeue(RequestInterface $request)
    {
        $this->dispatch('mock.request', array('plugin' => $this, 'request' => $request));

        $item = array_shift($this->queue);
        if ($item instanceof Response) {
            if ($this->readBodies && $request instanceof EntityEnclosingRequestInterface) {
                $request->getEventDispatcher()->addListener('request.sent', $f = function (Event $event) use (&$f) {
                    while ($data = $event['request']->getBody()->read(8096));
                    // Remove the listener after one-time use
                    $event['request']->getEventDispatcher()->removeListener('request.sent', $f);
                });
            }
            $request->setResponse($item);
        } elseif ($item instanceof CurlException) {
            // Emulates exceptions encountered while transferring requests
            $item->setRequest($request);
            $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $item));
            // Only throw if the exception wasn't handled
            if ($state == RequestInterface::STATE_ERROR) {
                throw $item;
            }
        }

        return $this;
    }

    /**
     * Clear the array of received requests
     */
    public function flush()
    {
        $this->received = array();
    }

    /**
     * Get an array of requests that were mocked by this plugin
     *
     * @return array
     */
    public function getReceivedRequests()
    {
        return $this->received;
    }

    /**
     * Called when a request is about to be sent
     *
     * @param Event $event
     * @throws \OutOfBoundsException When queue is empty
     */
    public function onRequestBeforeSend(Event $event)
    {
        if (!$this->queue) {
            throw new \OutOfBoundsException('Mock queue is empty');
        }

        $request = $event['request'];
        $this->received[] = $request;
        // Detach the filter from the client so it's a one-time use
        if ($this->temporary && count($this->queue) == 1 && $request->getClient()) {
            $request->getClient()->getEventDispatcher()->removeSubscriber($this);
        }
        $this->dequeue($request);
    }
}
<?php

namespace Guzzle\Plugin\Oauth;

use Guzzle\Common\Event;
use Guzzle\Common\Collection;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\QueryString;
use Guzzle\Http\Url;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * OAuth signing plugin
 * @link http://oauth.net/core/1.0/#rfc.section.9.1.1
 */
class OauthPlugin implements EventSubscriberInterface
{
    /**
     * Consumer request method constants. See http://oauth.net/core/1.0/#consumer_req_param
     */
    const REQUEST_METHOD_HEADER = 'header';
    const REQUEST_METHOD_QUERY  = 'query';

    /** @var Collection Configuration settings */
    protected $config;

    /**
     * Create a new OAuth 1.0 plugin
     *
     * @param array $config Configuration array containing these parameters:
     *     - string 'request_method'       Consumer request method. Use the class constants.
     *     - string 'callback'             OAuth callback
     *     - string 'consumer_key'         Consumer key
     *     - string 'consumer_secret'      Consumer secret
     *     - string 'token'                Token
     *     - string 'token_secret'         Token secret
     *     - string 'verifier'             OAuth verifier.
     *     - string 'version'              OAuth version.  Defaults to 1.0
     *     - string 'signature_method'     Custom signature method
     *     - bool   'disable_post_params'  Set to true to prevent POST parameters from being signed
     *     - array|Closure 'signature_callback' Custom signature callback that accepts a string to sign and a signing key
     */
    public function __construct($config)
    {
        $this->config = Collection::fromConfig($config, array(
            'version' => '1.0',
            'request_method' => self::REQUEST_METHOD_HEADER,
            'consumer_key' => 'anonymous',
            'consumer_secret' => 'anonymous',
            'signature_method' => 'HMAC-SHA1',
            'signature_callback' => function($stringToSign, $key) {
                return hash_hmac('sha1', $stringToSign, $key, true);
            }
        ), array(
            'signature_method', 'signature_callback', 'version',
            'consumer_key', 'consumer_secret'
        ));
    }

    public static function getSubscribedEvents()
    {
        return array(
            'request.before_send' => array('onRequestBeforeSend', -1000)
        );
    }

    /**
     * Request before-send event handler
     *
     * @param Event $event Event received
     * @return array
     * @throws \InvalidArgumentException
     */
    public function onRequestBeforeSend(Event $event)
    {
        $timestamp = $this->getTimestamp($event);
        $request = $event['request'];
        $nonce = $this->generateNonce($request);
        $authorizationParams = $this->getOauthParams($timestamp, $nonce);
        $authorizationParams['oauth_signature']  = $this->getSignature($request, $timestamp, $nonce);

        switch ($this->config['request_method']) {
            case self::REQUEST_METHOD_HEADER:
                $request->setHeader(
                    'Authorization',
                    $this->buildAuthorizationHeader($authorizationParams)
                );
                break;
            case self::REQUEST_METHOD_QUERY:
                foreach ($authorizationParams as $key => $value) {
                    $request->getQuery()->set($key, $value);
                }
                break;
            default:
                throw new \InvalidArgumentException(sprintf(
                    'Invalid consumer method "%s"',
                    $this->config['request_method']
                ));
        }

        return $authorizationParams;
    }

    /**
     * Builds the Authorization header for a request
     *
     * @param array $authorizationParams Associative array of authorization parameters
     *
     * @return string
     */
    private function buildAuthorizationHeader($authorizationParams)
    {
        $authorizationString = 'OAuth ';
        foreach ($authorizationParams as $key => $val) {
            if ($val) {
                $authorizationString .= $key . '="' . urlencode($val) . '", ';
            }
        }

        return substr($authorizationString, 0, -2);
    }

    /**
     * Calculate signature for request
     *
     * @param RequestInterface $request   Request to generate a signature for
     * @param integer          $timestamp Timestamp to use for nonce
     * @param string           $nonce
     *
     * @return string
     */
    public function getSignature(RequestInterface $request, $timestamp, $nonce)
    {
        $string = $this->getStringToSign($request, $timestamp, $nonce);
        $key = urlencode($this->config['consumer_secret']) . '&' . urlencode($this->config['token_secret']);

        return base64_encode(call_user_func($this->config['signature_callback'], $string, $key));
    }

    /**
     * Calculate string to sign
     *
     * @param RequestInterface $request   Request to generate a signature for
     * @param int              $timestamp Timestamp to use for nonce
     * @param string           $nonce
     *
     * @return string
     */
    public function getStringToSign(RequestInterface $request, $timestamp, $nonce)
    {
        $params = $this->getParamsToSign($request, $timestamp, $nonce);

        // Convert booleans to strings.
        $params = $this->prepareParameters($params);

        // Build signing string from combined params
        $parameterString = clone $request->getQuery();
        $parameterString->replace($params);

        $url = Url::factory($request->getUrl())->setQuery('')->setFragment(null);

        return strtoupper($request->getMethod()) . '&'
             . rawurlencode($url) . '&'
             . rawurlencode((string) $parameterString);
    }

    /**
     * Get the oauth parameters as named by the oauth spec
     *
     * @param $timestamp
     * @param $nonce
     * @return Collection
     */
    protected function getOauthParams($timestamp, $nonce)
    {
        $params = new Collection(array(
            'oauth_consumer_key'     => $this->config['consumer_key'],
            'oauth_nonce'            => $nonce,
            'oauth_signature_method' => $this->config['signature_method'],
            'oauth_timestamp'        => $timestamp,
        ));

        // Optional parameters should not be set if they have not been set in the config as
        // the parameter may be considered invalid by the Oauth service.
        $optionalParams = array(
            'callback'  => 'oauth_callback',
            'token'     => 'oauth_token',
            'verifier'  => 'oauth_verifier',
            'version'   => 'oauth_version'
        );

        foreach ($optionalParams as $optionName => $oauthName) {
            if (isset($this->config[$optionName]) == true) {
                $params[$oauthName] = $this->config[$optionName];
            }
        }

        return $params;
    }

    /**
     * Get all of the parameters required to sign a request including:
     * * The oauth params
     * * The request GET params
     * * The params passed in the POST body (with a content-type of application/x-www-form-urlencoded)
     *
     * @param RequestInterface $request   Request to generate a signature for
     * @param integer          $timestamp Timestamp to use for nonce
     * @param string           $nonce
     *
     * @return array
     */
    public function getParamsToSign(RequestInterface $request, $timestamp, $nonce)
    {
        $params = $this->getOauthParams($timestamp, $nonce);

        // Add query string parameters
        $params->merge($request->getQuery());

        // Add POST fields to signing string if required
        if ($this->shouldPostFieldsBeSigned($request))
        {
            $params->merge($request->getPostFields());
        }

        // Sort params
        $params = $params->toArray();
        uksort($params, 'strcmp');

        return $params;
    }

    /**
     * Decide whether the post fields should be added to the base string that Oauth signs.
     * This implementation is correct. Non-conformant APIs may require that this method be
     * overwritten e.g. the Flickr API incorrectly adds the post fields when the Content-Type
     * is 'application/x-www-form-urlencoded'
     *
     * @param $request
     * @return bool Whether the post fields should be signed or not
     */
    public function shouldPostFieldsBeSigned($request)
    {
        if (!$this->config->get('disable_post_params') &&
            $request instanceof EntityEnclosingRequestInterface &&
            false !== strpos($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded'))
        {
            return true;
        }

        return false;
    }

    /**
     * Returns a Nonce Based on the unique id and URL. This will allow for multiple requests in parallel with the same
     * exact timestamp to use separate nonce's.
     *
     * @param RequestInterface $request Request to generate a nonce for
     *
     * @return string
     */
    public function generateNonce(RequestInterface $request)
    {
        return sha1(uniqid('', true) . $request->getUrl());
    }

    /**
     * Gets timestamp from event or create new timestamp
     *
     * @param Event $event Event containing contextual information
     *
     * @return int
     */
    public function getTimestamp(Event $event)
    {
       return $event['timestamp'] ?: time();
    }

    /**
     * Convert booleans to strings, removed unset parameters, and sorts the array
     *
     * @param array $data Data array
     *
     * @return array
     */
    protected function prepareParameters($data)
    {
        ksort($data);
        foreach ($data as $key => &$value) {
            switch (gettype($value)) {
                case 'NULL':
                    unset($data[$key]);
                    break;
                case 'array':
                    $data[$key] = self::prepareParameters($value);
                    break;
                case 'boolean':
                    $data[$key] = $value ? 'true' : 'false';
                    break;
            }
        }

        return $data;
    }
}
<?php

namespace Guzzle\Service;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;

/**
 * Abstract config loader
 */
abstract class AbstractConfigLoader implements ConfigLoaderInterface
{
    /** @var array Array of aliases for actual filenames */
    protected $aliases = array();

    /** @var array Hash of previously loaded filenames */
    protected $loadedFiles = array();

    /** @var array JSON error code mappings */
    protected static $jsonErrors = array(
        JSON_ERROR_NONE => 'JSON_ERROR_NONE - No errors',
        JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded',
        JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch',
        JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found',
        JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON',
        JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded'
    );

    public function load($config, array $options = array())
    {
        // Reset the array of loaded files because this is a new config
        $this->loadedFiles = array();

        if (is_string($config)) {
            $config = $this->loadFile($config);
        } elseif (!is_array($config)) {
            throw new InvalidArgumentException('Unknown type passed to configuration loader: ' . gettype($config));
        } else {
            $this->mergeIncludes($config);
        }

        return $this->build($config, $options);
    }

    /**
     * Add an include alias to the loader
     *
     * @param string $filename Filename to alias (e.g. _foo)
     * @param string $alias    Actual file to use (e.g. /path/to/foo.json)
     *
     * @return self
     */
    public function addAlias($filename, $alias)
    {
        $this->aliases[$filename] = $alias;

        return $this;
    }

    /**
     * Remove an alias from the loader
     *
     * @param string $alias Alias to remove
     *
     * @return self
     */
    public function removeAlias($alias)
    {
        unset($this->aliases[$alias]);

        return $this;
    }

    /**
     * Perform the parsing of a config file and create the end result
     *
     * @param array $config  Configuration data
     * @param array $options Options to use when building
     *
     * @return mixed
     */
    protected abstract function build($config, array $options);

    /**
     * Load a configuration file (can load JSON or PHP files that return an array when included)
     *
     * @param string $filename File to load
     *
     * @return array
     * @throws InvalidArgumentException
     * @throws RuntimeException when the JSON cannot be parsed
     */
    protected function loadFile($filename)
    {
        if (isset($this->aliases[$filename])) {
            $filename = $this->aliases[$filename];
        }

        switch (pathinfo($filename, PATHINFO_EXTENSION)) {
            case 'js':
            case 'json':
                $level = error_reporting(0);
                $json = file_get_contents($filename);
                error_reporting($level);

                if ($json === false) {
                    $err = error_get_last();
                    throw new InvalidArgumentException("Unable to open {$filename}: " . $err['message']);
                }

                $config = json_decode($json, true);
                // Throw an exception if there was an error loading the file
                if ($error = json_last_error()) {
                    $message = isset(self::$jsonErrors[$error]) ? self::$jsonErrors[$error] : 'Unknown error';
                    throw new RuntimeException("Error loading JSON data from {$filename}: ({$error}) - {$message}");
                }
                break;
            case 'php':
                if (!is_readable($filename)) {
                    throw new InvalidArgumentException("Unable to open {$filename} for reading");
                }
                $config = require $filename;
                if (!is_array($config)) {
                    throw new InvalidArgumentException('PHP files must return an array of configuration data');
                }
                break;
            default:
                throw new InvalidArgumentException('Unknown file extension: ' . $filename);
        }

        // Keep track of this file being loaded to prevent infinite recursion
        $this->loadedFiles[$filename] = true;

        // Merge include files into the configuration array
        $this->mergeIncludes($config, dirname($filename));

        return $config;
    }

    /**
     * Merges in all include files
     *
     * @param array  $config   Config data that contains includes
     * @param string $basePath Base path to use when a relative path is encountered
     *
     * @return array Returns the merged and included data
     */
    protected function mergeIncludes(&$config, $basePath = null)
    {
        if (!empty($config['includes'])) {
            foreach ($config['includes'] as &$path) {
                // Account for relative paths
                if ($path[0] != DIRECTORY_SEPARATOR && !isset($this->aliases[$path]) && $basePath) {
                    $path = "{$basePath}/{$path}";
                }
                // Don't load the same files more than once
                if (!isset($this->loadedFiles[$path])) {
                    $this->loadedFiles[$path] = true;
                    $config = $this->mergeData($this->loadFile($path), $config);
                }
            }
        }
    }

    /**
     * Default implementation for merging two arrays of data (uses array_merge_recursive)
     *
     * @param array $a Original data
     * @param array $b Data to merge into the original and overwrite existing values
     *
     * @return array
     */
    protected function mergeData(array $a, array $b)
    {
        return array_merge_recursive($a, $b);
    }
}
<?php

namespace Guzzle\Service\Builder;

use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Service\ClientInterface;
use Guzzle\Service\Exception\ServiceBuilderException;
use Guzzle\Service\Exception\ServiceNotFoundException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * {@inheritdoc}
 *
 * Clients and data can be set, retrieved, and removed by accessing the service builder like an associative array.
 */
class ServiceBuilder extends AbstractHasDispatcher implements ServiceBuilderInterface, \ArrayAccess, \Serializable
{
    /** @var array Service builder configuration data */
    protected $builderConfig = array();

    /** @var array Instantiated client objects */
    protected $clients = array();

    /** @var ServiceBuilderLoader Cached instance of the service builder loader */
    protected static $cachedFactory;

    /** @var array Plugins to attach to each client created by the service builder */
    protected $plugins = array();

    /**
     * Create a new ServiceBuilder using configuration data sourced from an
     * array, .js|.json or .php file.
     *
     * @param array|string $config           The full path to an .json|.js or .php file, or an associative array
     * @param array        $globalParameters Array of global parameters to pass to every service as it is instantiated.
     *
     * @return ServiceBuilderInterface
     * @throws ServiceBuilderException if a file cannot be opened
     * @throws ServiceNotFoundException when trying to extend a missing client
     */
    public static function factory($config = null, array $globalParameters = array())
    {
        // @codeCoverageIgnoreStart
        if (!static::$cachedFactory) {
            static::$cachedFactory = new ServiceBuilderLoader();
        }
        // @codeCoverageIgnoreEnd

        return self::$cachedFactory->load($config, $globalParameters);
    }

    /**
     * @param array $serviceBuilderConfig Service configuration settings:
     *     - name: Name of the service
     *     - class: Client class to instantiate using a factory method
     *     - params: array of key value pair configuration settings for the builder
     */
    public function __construct(array $serviceBuilderConfig = array())
    {
        $this->builderConfig = $serviceBuilderConfig;
    }

    public static function getAllEvents()
    {
        return array('service_builder.create_client');
    }

    public function unserialize($serialized)
    {
        $this->builderConfig = json_decode($serialized, true);
    }

    public function serialize()
    {
        return json_encode($this->builderConfig);
    }

    /**
     * Attach a plugin to every client created by the builder
     *
     * @param EventSubscriberInterface $plugin Plugin to attach to each client
     *
     * @return self
     */
    public function addGlobalPlugin(EventSubscriberInterface $plugin)
    {
        $this->plugins[] = $plugin;

        return $this;
    }

    /**
     * Get data from the service builder without triggering the building of a service
     *
     * @param string $name Name of the service to retrieve
     *
     * @return array|null
     */
    public function getData($name)
    {
        return isset($this->builderConfig[$name]) ? $this->builderConfig[$name] : null;
    }

    public function get($name, $throwAway = false)
    {
        if (!isset($this->builderConfig[$name])) {

            // Check to see if arbitrary data is being referenced
            if (isset($this->clients[$name])) {
                return $this->clients[$name];
            }

            // Check aliases and return a match if found
            foreach ($this->builderConfig as $actualName => $config) {
                if (isset($config['alias']) && $config['alias'] == $name) {
                    return $this->get($actualName, $throwAway);
                }
            }
            throw new ServiceNotFoundException('No service is registered as ' . $name);
        }

        if (!$throwAway && isset($this->clients[$name])) {
            return $this->clients[$name];
        }

        $builder =& $this->builderConfig[$name];

        // Convert references to the actual client
        foreach ($builder['params'] as &$v) {
            if (is_string($v) && substr($v, 0, 1) == '{' && substr($v, -1) == '}') {
                $v = $this->get(trim($v, '{} '));
            }
        }

        // Get the configured parameters and merge in any parameters provided for throw-away clients
        $config = $builder['params'];
        if (is_array($throwAway)) {
            $config = $throwAway + $config;
        }

        $client = $builder['class']::factory($config);

        if (!$throwAway) {
            $this->clients[$name] = $client;
        }

        if ($client instanceof ClientInterface) {
            foreach ($this->plugins as $plugin) {
                $client->addSubscriber($plugin);
            }
            // Dispatch an event letting listeners know a client was created
            $this->dispatch('service_builder.create_client', array('client' => $client));
        }

        return $client;
    }

    public function set($key, $service)
    {
        if (is_array($service) && isset($service['class']) && isset($service['params'])) {
            $this->builderConfig[$key] = $service;
        } else {
            $this->clients[$key] = $service;
        }

        return $this;
    }

    public function offsetSet($offset, $value)
    {
        $this->set($offset, $value);
    }

    public function offsetUnset($offset)
    {
        unset($this->builderConfig[$offset]);
        unset($this->clients[$offset]);
    }

    public function offsetExists($offset)
    {
        return isset($this->builderConfig[$offset]) || isset($this->clients[$offset]);
    }

    public function offsetGet($offset)
    {
        return $this->get($offset);
    }
}
<?php

namespace Guzzle\Service\Builder;

use Guzzle\Service\Exception\ServiceNotFoundException;

/**
 * Service builder used to store and build clients or arbitrary data. Client configuration data can be supplied to tell
 * the service builder how to create and cache {@see \Guzzle\Service\ClientInterface} objects. Arbitrary data can be
 * supplied and accessed from a service builder. Arbitrary data and other clients can be referenced by name in client
 * configuration arrays to make them input for building other clients (e.g. "{key}").
 */
interface ServiceBuilderInterface
{
    /**
     * Get a ClientInterface object or arbitrary data from the service builder
     *
     * @param string     $name      Name of the registered service or data to retrieve
     * @param bool|array $throwAway Only pertains to retrieving client objects built using a configuration array.
     *                              Set to TRUE to not store the client for later retrieval from the ServiceBuilder.
     *                              If an array is specified, that data will overwrite the configured params of the
     *                              client if the client implements {@see \Guzzle\Common\FromConfigInterface} and will
     *                              not store the client for later retrieval.
     *
     * @return \Guzzle\Service\ClientInterface|mixed
     * @throws ServiceNotFoundException when a client or data cannot be found by the given name
     */
    public function get($name, $throwAway = false);

    /**
     * Register a service or arbitrary data by name with the service builder
     *
     * @param string $key     Name of the client or data to register
     * @param mixed  $service Client configuration array or arbitrary data to register. The client configuration array
     *                        must include a 'class' (string) and 'params' (array) key.
     *
     * @return ServiceBuilderInterface
     */
    public function set($key, $service);
}
<?php

namespace Guzzle\Service\Builder;

use Guzzle\Service\AbstractConfigLoader;
use Guzzle\Service\Exception\ServiceNotFoundException;

/**
 * Service builder config loader
 */
class ServiceBuilderLoader extends AbstractConfigLoader
{
    protected function build($config, array $options)
    {
        // A service builder class can be specified in the class field
        $class = !empty($config['class']) ? $config['class'] : __NAMESPACE__ . '\\ServiceBuilder';

        // Account for old style configs that do not have a services array
        $services = isset($config['services']) ? $config['services'] : $config;

        // Validate the configuration and handle extensions
        foreach ($services as $name => &$service) {

            $service['params'] = isset($service['params']) ? $service['params'] : array();

            // Check if this client builder extends another client
            if (!empty($service['extends'])) {

                // Make sure that the service it's extending has been defined
                if (!isset($services[$service['extends']])) {
                    throw new ServiceNotFoundException(
                        "{$name} is trying to extend a non-existent service: {$service['extends']}"
                    );
                }

                $extended = &$services[$service['extends']];

                // Use the correct class attribute
                if (empty($service['class'])) {
                    $service['class'] = isset($extended['class']) ? $extended['class'] : '';
                }
                if ($extendsParams = isset($extended['params']) ? $extended['params'] : false) {
                    $service['params'] = $service['params'] + $extendsParams;
                }
            }

            // Overwrite default values with global parameter values
            if (!empty($options)) {
                $service['params'] = $options + $service['params'];
            }

            $service['class'] = isset($service['class']) ? $service['class'] : '';
        }

        return new $class($services);
    }

    protected function mergeData(array $a, array $b)
    {
        $result = $b + $a;

        // Merge services using a recursive union of arrays
        if (isset($a['services']) && $b['services']) {

            // Get a union of the services of the two arrays
            $result['services'] = $b['services'] + $a['services'];

            // Merge each service in using a union of the two arrays
            foreach ($result['services'] as $name => &$service) {

                // By default, services completely override a previously defined service unless it extends itself
                if (isset($a['services'][$name]['extends'])
                    && isset($b['services'][$name]['extends'])
                    && $b['services'][$name]['extends'] == $name
                ) {
                    $service += $a['services'][$name];
                    // Use the `extends` attribute of the parent
                    $service['extends'] = $a['services'][$name]['extends'];
                    // Merge parameters using a union if both have parameters
                    if (isset($a['services'][$name]['params'])) {
                        $service['params'] += $a['services'][$name]['params'];
                    }
                }
            }
        }

        return $result;
    }
}
<?php

namespace Guzzle\Service;

use Guzzle\Cache\CacheAdapterInterface;

/**
 * Decorator that adds caching to a service description loader
 */
class CachingConfigLoader implements ConfigLoaderInterface
{
    /** @var ConfigLoaderInterface */
    protected $loader;

    /** @var CacheAdapterInterface */
    protected $cache;

    /**
     * @param ConfigLoaderInterface $loader Loader used to load the config when there is a cache miss
     * @param CacheAdapterInterface $cache  Object used to cache the loaded result
     */
    public function __construct(ConfigLoaderInterface $loader, CacheAdapterInterface $cache)
    {
        $this->loader = $loader;
        $this->cache = $cache;
    }

    public function load($config, array $options = array())
    {
        if (!is_string($config)) {
            $key = false;
        } else {
            $key = 'loader_' . crc32($config);
            if ($result = $this->cache->fetch($key)) {
                return $result;
            }
        }

        $result = $this->loader->load($config, $options);
        if ($key) {
            $this->cache->save($key, $result);
        }

        return $result;
    }
}
<?php

namespace Guzzle\Service;

use Guzzle\Common\Collection;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\BadMethodCallException;
use Guzzle\Common\Version;
use Guzzle\Inflection\InflectorInterface;
use Guzzle\Inflection\Inflector;
use Guzzle\Http\Client as HttpClient;
use Guzzle\Http\Exception\MultiTransferException;
use Guzzle\Service\Exception\CommandTransferException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Command\Factory\CompositeFactory;
use Guzzle\Service\Command\Factory\FactoryInterface as CommandFactoryInterface;
use Guzzle\Service\Resource\ResourceIteratorClassFactory;
use Guzzle\Service\Resource\ResourceIteratorFactoryInterface;
use Guzzle\Service\Description\ServiceDescriptionInterface;

/**
 * Client object for executing commands on a web service.
 */
class Client extends HttpClient implements ClientInterface
{
    const COMMAND_PARAMS = 'command.params';

    /** @var ServiceDescriptionInterface Description of the service and possible commands */
    protected $serviceDescription;

    /** @var CommandFactoryInterface */
    protected $commandFactory;

    /** @var ResourceIteratorFactoryInterface */
    protected $resourceIteratorFactory;

    /** @var InflectorInterface Inflector associated with the service/client */
    protected $inflector;

    /**
     * Basic factory method to create a new client. Extend this method in subclasses to build more complex clients.
     *
     * @param array|Collection $config Configuration data
     *
     * @return Client
     */
    public static function factory($config = array())
    {
        return new static(isset($config['base_url']) ? $config['base_url'] : null, $config);
    }

    public static function getAllEvents()
    {
        return array_merge(HttpClient::getAllEvents(), array(
            'client.command.create',
            'command.before_prepare',
            'command.after_prepare',
            'command.before_send',
            'command.after_send',
            'command.parse_response'
        ));
    }

    /**
     * Magic method used to retrieve a command
     *
     * @param string $method Name of the command object to instantiate
     * @param array  $args   Arguments to pass to the command
     *
     * @return mixed Returns the result of the command
     * @throws BadMethodCallException when a command is not found
     */
    public function __call($method, $args)
    {
        return $this->getCommand($method, isset($args[0]) ? $args[0] : array())->getResult();
    }

    public function getCommand($name, array $args = array())
    {
        // Add global client options to the command
        if ($options = $this->getConfig(self::COMMAND_PARAMS)) {
            $args += $options;
        }

        if (!($command = $this->getCommandFactory()->factory($name, $args))) {
            throw new InvalidArgumentException("Command was not found matching {$name}");
        }

        $command->setClient($this);
        $this->dispatch('client.command.create', array('client' => $this, 'command' => $command));

        return $command;
    }

    /**
     * Set the command factory used to create commands by name
     *
     * @param CommandFactoryInterface $factory Command factory
     *
     * @return self
     */
    public function setCommandFactory(CommandFactoryInterface $factory)
    {
        $this->commandFactory = $factory;

        return $this;
    }

    /**
     * Set the resource iterator factory associated with the client
     *
     * @param ResourceIteratorFactoryInterface $factory Resource iterator factory
     *
     * @return self
     */
    public function setResourceIteratorFactory(ResourceIteratorFactoryInterface $factory)
    {
        $this->resourceIteratorFactory = $factory;

        return $this;
    }

    public function getIterator($command, array $commandOptions = null, array $iteratorOptions = array())
    {
        if (!($command instanceof CommandInterface)) {
            $command = $this->getCommand($command, $commandOptions ?: array());
        }

        return $this->getResourceIteratorFactory()->build($command, $iteratorOptions);
    }

    public function execute($command)
    {
        if ($command instanceof CommandInterface) {
            $this->send($this->prepareCommand($command));
            $this->dispatch('command.after_send', array('command' => $command));
            return $command->getResult();
        } elseif (is_array($command) || $command instanceof \Traversable) {
            return $this->executeMultiple($command);
        } else {
            throw new InvalidArgumentException('Command must be a command or array of commands');
        }
    }

    public function setDescription(ServiceDescriptionInterface $service)
    {
        $this->serviceDescription = $service;

        if ($this->getCommandFactory() && $this->getCommandFactory() instanceof CompositeFactory) {
            $this->commandFactory->add(new Command\Factory\ServiceDescriptionFactory($service));
        }

        // If a baseUrl was set on the description, then update the client
        if ($baseUrl = $service->getBaseUrl()) {
            $this->setBaseUrl($baseUrl);
        }

        return $this;
    }

    public function getDescription()
    {
        return $this->serviceDescription;
    }

    /**
     * Set the inflector used with the client
     *
     * @param InflectorInterface $inflector Inflection object
     *
     * @return self
     */
    public function setInflector(InflectorInterface $inflector)
    {
        $this->inflector = $inflector;

        return $this;
    }

    /**
     * Get the inflector used with the client
     *
     * @return self
     */
    public function getInflector()
    {
        if (!$this->inflector) {
            $this->inflector = Inflector::getDefault();
        }

        return $this->inflector;
    }

    /**
     * Prepare a command for sending and get the RequestInterface object created by the command
     *
     * @param CommandInterface $command Command to prepare
     *
     * @return RequestInterface
     */
    protected function prepareCommand(CommandInterface $command)
    {
        // Set the client and prepare the command
        $request = $command->setClient($this)->prepare();
        // Set the state to new if the command was previously executed
        $request->setState(RequestInterface::STATE_NEW);
        $this->dispatch('command.before_send', array('command' => $command));

        return $request;
    }

    /**
     * Execute multiple commands in parallel
     *
     * @param array|Traversable $commands Array of CommandInterface objects to execute
     *
     * @return array Returns an array of the executed commands
     * @throws Exception\CommandTransferException
     */
    protected function executeMultiple($commands)
    {
        $requests = array();
        $commandRequests = new \SplObjectStorage();

        foreach ($commands as $command) {
            $request = $this->prepareCommand($command);
            $commandRequests[$request] = $command;
            $requests[] = $request;
        }

        try {
            $this->send($requests);
            foreach ($commands as $command) {
                $this->dispatch('command.after_send', array('command' => $command));
            }
            return $commands;
        } catch (MultiTransferException $failureException) {
            // Throw a CommandTransferException using the successful and failed commands
            $e = CommandTransferException::fromMultiTransferException($failureException);

            // Remove failed requests from the successful requests array and add to the failures array
            foreach ($failureException->getFailedRequests() as $request) {
                if (isset($commandRequests[$request])) {
                    $e->addFailedCommand($commandRequests[$request]);
                    unset($commandRequests[$request]);
                }
            }

            // Always emit the command after_send events for successful commands
            foreach ($commandRequests as $success) {
                $e->addSuccessfulCommand($commandRequests[$success]);
                $this->dispatch('command.after_send', array('command' => $commandRequests[$success]));
            }

            throw $e;
        }
    }

    protected function getResourceIteratorFactory()
    {
        if (!$this->resourceIteratorFactory) {
            // Build the default resource iterator factory if one is not set
            $clientClass = get_class($this);
            $prefix = substr($clientClass, 0, strrpos($clientClass, '\\'));
            $this->resourceIteratorFactory = new ResourceIteratorClassFactory(array(
                "{$prefix}\\Iterator",
                "{$prefix}\\Model"
            ));
        }

        return $this->resourceIteratorFactory;
    }

    /**
     * Get the command factory associated with the client
     *
     * @return CommandFactoryInterface
     */
    protected function getCommandFactory()
    {
        if (!$this->commandFactory) {
            $this->commandFactory = CompositeFactory::getDefaultChain($this);
        }

        return $this->commandFactory;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function enableMagicMethods($isEnabled)
    {
        Version::warn(__METHOD__ . ' is deprecated');
    }
}
<?php

namespace Guzzle\Service;

use Guzzle\Common\FromConfigInterface;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\ClientInterface as HttpClientInterface;
use Guzzle\Service\Exception\CommandTransferException;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\ServiceDescriptionInterface;
use Guzzle\Service\Resource\ResourceIteratorInterface;

/**
 * Client interface for executing commands on a web service.
 */
interface ClientInterface extends HttpClientInterface, FromConfigInterface
{
    /**
     * Get a command by name. First, the client will see if it has a service description and if the service description
     * defines a command by the supplied name. If no dynamic command is found, the client will look for a concrete
     * command class exists matching the name supplied. If neither are found, an InvalidArgumentException is thrown.
     *
     * @param string $name Name of the command to retrieve
     * @param array  $args Arguments to pass to the command
     *
     * @return CommandInterface
     * @throws InvalidArgumentException if no command can be found by name
     */
    public function getCommand($name, array $args = array());

    /**
     * Execute one or more commands
     *
     * @param CommandInterface|array|Traversable $command Command, array of commands or Traversable object containing commands to execute
     *
     * @return mixed Returns the result of the executed command or an array of commands if executing multiple commands
     * @throws InvalidArgumentException if an invalid command is passed
     * @throws CommandTransferException if an exception is encountered when transferring multiple commands
     */
    public function execute($command);

    /**
     * Set the service description of the client
     *
     * @param ServiceDescriptionInterface $service Service description
     *
     * @return ClientInterface
     */
    public function setDescription(ServiceDescriptionInterface $service);

    /**
     * Get the service description of the client
     *
     * @return ServiceDescriptionInterface|null
     */
    public function getDescription();

    /**
     * Get a resource iterator from the client.
     *
     * @param string|CommandInterface $command         Command class or command name.
     * @param array                   $commandOptions  Command options used when creating commands.
     * @param array                   $iteratorOptions Iterator options passed to the iterator when it is instantiated.
     *
     * @return ResourceIteratorInterface
     */
    public function getIterator($command, array $commandOptions = null, array $iteratorOptions = array());
}
<?php

namespace Guzzle\Service\Command;

use Guzzle\Common\Collection;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Curl\CurlHandle;
use Guzzle\Service\Client;
use Guzzle\Service\ClientInterface;
use Guzzle\Service\Description\Operation;
use Guzzle\Service\Description\OperationInterface;
use Guzzle\Service\Description\ValidatorInterface;
use Guzzle\Service\Description\SchemaValidator;
use Guzzle\Service\Exception\CommandException;
use Guzzle\Service\Exception\ValidationException;

/**
 * Command object to handle preparing and processing client requests and responses of the requests
 */
abstract class AbstractCommand extends Collection implements CommandInterface
{
    // @deprecated: Option used to specify custom headers to add to the generated request
    const HEADERS_OPTION = 'command.headers';
    // @deprecated: Option used to add an onComplete method to a command
    const ON_COMPLETE = 'command.on_complete';
    // @deprecated: Option used to change the entity body used to store a response
    const RESPONSE_BODY = 'command.response_body';

    // Option used to add request options to the request created by a command
    const REQUEST_OPTIONS = 'command.request_options';
    // command values to not count as additionalParameters
    const HIDDEN_PARAMS = 'command.hidden_params';
    // Option used to disable any pre-sending command validation
    const DISABLE_VALIDATION = 'command.disable_validation';
    // Option used to override how a command result will be formatted
    const RESPONSE_PROCESSING = 'command.response_processing';
    // Different response types that commands can use
    const TYPE_RAW = 'raw';
    const TYPE_MODEL = 'model';
    const TYPE_NO_TRANSLATION = 'no_translation';

    /** @var ClientInterface Client object used to execute the command */
    protected $client;

    /** @var RequestInterface The request object associated with the command */
    protected $request;

    /** @var mixed The result of the command */
    protected $result;

    /** @var OperationInterface API information about the command */
    protected $operation;

    /** @var mixed callable */
    protected $onComplete;

    /** @var ValidatorInterface Validator used to prepare and validate properties against a JSON schema */
    protected $validator;

    /**
     * @param array|Collection   $parameters Collection of parameters to set on the command
     * @param OperationInterface $operation Command definition from description
     */
    public function __construct($parameters = array(), OperationInterface $operation = null)
    {
        parent::__construct($parameters);
        $this->operation = $operation ?: $this->createOperation();
        foreach ($this->operation->getParams() as $name => $arg) {
            $currentValue = $this[$name];
            $configValue = $arg->getValue($currentValue);
            // If default or static values are set, then this should always be updated on the config object
            if ($currentValue !== $configValue) {
                $this[$name] = $configValue;
            }
        }

        $headers = $this[self::HEADERS_OPTION];
        if (!$headers instanceof Collection) {
            $this[self::HEADERS_OPTION] = new Collection((array) $headers);
        }

        // You can set a command.on_complete option in your parameters to set an onComplete callback
        if ($onComplete = $this['command.on_complete']) {
            unset($this['command.on_complete']);
            $this->setOnComplete($onComplete);
        }

        // Set the hidden additional parameters
        if (!$this[self::HIDDEN_PARAMS]) {
            $this[self::HIDDEN_PARAMS] = array(
                self::HEADERS_OPTION,
                self::RESPONSE_PROCESSING,
                self::HIDDEN_PARAMS,
                self::REQUEST_OPTIONS
            );
        }

        $this->init();
    }

    /**
     * Custom clone behavior
     */
    public function __clone()
    {
        $this->request = null;
        $this->result = null;
    }

    /**
     * Execute the command in the same manner as calling a function
     *
     * @return mixed Returns the result of {@see AbstractCommand::execute}
     */
    public function __invoke()
    {
        return $this->execute();
    }

    public function getName()
    {
        return $this->operation->getName();
    }

    /**
     * Get the API command information about the command
     *
     * @return OperationInterface
     */
    public function getOperation()
    {
        return $this->operation;
    }

    public function setOnComplete($callable)
    {
        if (!is_callable($callable)) {
            throw new InvalidArgumentException('The onComplete function must be callable');
        }

        $this->onComplete = $callable;

        return $this;
    }

    public function execute()
    {
        if (!$this->client) {
            throw new CommandException('A client must be associated with the command before it can be executed.');
        }

        return $this->client->execute($this);
    }

    public function getClient()
    {
        return $this->client;
    }

    public function setClient(ClientInterface $client)
    {
        $this->client = $client;

        return $this;
    }

    public function getRequest()
    {
        if (!$this->request) {
            throw new CommandException('The command must be prepared before retrieving the request');
        }

        return $this->request;
    }

    public function getResponse()
    {
        if (!$this->isExecuted()) {
            $this->execute();
        }

        return $this->request->getResponse();
    }

    public function getResult()
    {
        if (!$this->isExecuted()) {
            $this->execute();
        }

        if (null === $this->result) {
            $this->process();
            // Call the onComplete method if one is set
            if ($this->onComplete) {
                call_user_func($this->onComplete, $this);
            }
        }

        return $this->result;
    }

    public function setResult($result)
    {
        $this->result = $result;

        return $this;
    }

    public function isPrepared()
    {
        return $this->request !== null;
    }

    public function isExecuted()
    {
        return $this->request !== null && $this->request->getState() == 'complete';
    }

    public function prepare()
    {
        if (!$this->isPrepared()) {
            if (!$this->client) {
                throw new CommandException('A client must be associated with the command before it can be prepared.');
            }

            // If no response processing value was specified, then attempt to use the highest level of processing
            if (!isset($this[self::RESPONSE_PROCESSING])) {
                $this[self::RESPONSE_PROCESSING] = self::TYPE_MODEL;
            }

            // Notify subscribers of the client that the command is being prepared
            $this->client->dispatch('command.before_prepare', array('command' => $this));

            // Fail on missing required arguments, and change parameters via filters
            $this->validate();
            // Delegate to the subclass that implements the build method
            $this->build();

            // Add custom request headers set on the command
            if ($headers = $this[self::HEADERS_OPTION]) {
                foreach ($headers as $key => $value) {
                    $this->request->setHeader($key, $value);
                }
            }

            // Add any curl options to the request
            if ($options = $this[Client::CURL_OPTIONS]) {
                $this->request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($options));
            }

            // Set a custom response body
            if ($responseBody = $this[self::RESPONSE_BODY]) {
                $this->request->setResponseBody($responseBody);
            }

            $this->client->dispatch('command.after_prepare', array('command' => $this));
        }

        return $this->request;
    }

    /**
     * Set the validator used to validate and prepare command parameters and nested JSON schemas. If no validator is
     * set, then the command will validate using the default {@see SchemaValidator}.
     *
     * @param ValidatorInterface $validator Validator used to prepare and validate properties against a JSON schema
     *
     * @return self
     */
    public function setValidator(ValidatorInterface $validator)
    {
        $this->validator = $validator;

        return $this;
    }

    public function getRequestHeaders()
    {
        return $this[self::HEADERS_OPTION];
    }

    /**
     * Initialize the command (hook that can be implemented in subclasses)
     */
    protected function init() {}

    /**
     * Create the request object that will carry out the command
     */
    abstract protected function build();

    /**
     * Hook used to create an operation for concrete commands that are not associated with a service description
     *
     * @return OperationInterface
     */
    protected function createOperation()
    {
        return new Operation(array('name' => get_class($this)));
    }

    /**
     * Create the result of the command after the request has been completed.
     * Override this method in subclasses to customize this behavior
     */
    protected function process()
    {
        $this->result = $this[self::RESPONSE_PROCESSING] != self::TYPE_RAW
            ? DefaultResponseParser::getInstance()->parse($this)
            : $this->request->getResponse();
    }

    /**
     * Validate and prepare the command based on the schema and rules defined by the command's Operation object
     *
     * @throws ValidationException when validation errors occur
     */
    protected function validate()
    {
        // Do not perform request validation/transformation if it is disable
        if ($this[self::DISABLE_VALIDATION]) {
            return;
        }

        $errors = array();
        $validator = $this->getValidator();
        foreach ($this->operation->getParams() as $name => $schema) {
            $value = $this[$name];
            if (!$validator->validate($schema, $value)) {
                $errors = array_merge($errors, $validator->getErrors());
            } elseif ($value !== $this[$name]) {
                // Update the config value if it changed and no validation errors were encountered
                $this->data[$name] = $value;
            }
        }

        // Validate additional parameters
        $hidden = $this[self::HIDDEN_PARAMS];

        if ($properties = $this->operation->getAdditionalParameters()) {
            foreach ($this->toArray() as $name => $value) {
                // It's only additional if it isn't defined in the schema
                if (!$this->operation->hasParam($name) && !in_array($name, $hidden)) {
                    // Always set the name so that error messages are useful
                    $properties->setName($name);
                    if (!$validator->validate($properties, $value)) {
                        $errors = array_merge($errors, $validator->getErrors());
                    } elseif ($value !== $this[$name]) {
                        $this->data[$name] = $value;
                    }
                }
            }
        }

        if (!empty($errors)) {
            $e = new ValidationException('Validation errors: ' . implode("\n", $errors));
            $e->setErrors($errors);
            throw $e;
        }
    }

    /**
     * Get the validator used to prepare and validate properties. If no validator has been set on the command, then
     * the default {@see SchemaValidator} will be used.
     *
     * @return ValidatorInterface
     */
    protected function getValidator()
    {
        if (!$this->validator) {
            $this->validator = SchemaValidator::getInstance();
        }

        return $this->validator;
    }

    /**
     * Get array of any validation errors
     * If no validator has been set then return false
     */
    public function getValidationErrors()
    {
        if (!$this->validator) {
            return false;
        }

        return $this->validator->getErrors();
    }
}
<?php

namespace Guzzle\Service\Command;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\UnexpectedValueException;
use Guzzle\Http\Message\RequestInterface;

/**
 * A ClosureCommand is a command that allows dynamic commands to be created at runtime using a closure to prepare the
 * request. A closure key and \Closure value must be passed to the command in the constructor. The closure must
 * accept the command object as an argument.
 */
class ClosureCommand extends AbstractCommand
{
    /**
     * {@inheritdoc}
     * @throws InvalidArgumentException if a closure was not passed
     */
    protected function init()
    {
        if (!$this['closure']) {
            throw new InvalidArgumentException('A closure must be passed in the parameters array');
        }
    }

    /**
     * {@inheritdoc}
     * @throws UnexpectedValueException If the closure does not return a request
     */
    protected function build()
    {
        $closure = $this['closure'];
        /** @var $closure \Closure */
        $this->request = $closure($this, $this->operation);

        if (!$this->request || !$this->request instanceof RequestInterface) {
            throw new UnexpectedValueException('Closure command did not return a RequestInterface object');
        }
    }
}
<?php

namespace Guzzle\Service\Command;

use Guzzle\Common\Collection;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Exception\CommandException;
use Guzzle\Service\Description\OperationInterface;
use Guzzle\Service\ClientInterface;
use Guzzle\Common\ToArrayInterface;

/**
 * A command object that contains parameters that can be modified and accessed like an array and turned into an array
 */
interface CommandInterface extends \ArrayAccess, ToArrayInterface
{
    /**
     * Get the short form name of the command
     *
     * @return string
     */
    public function getName();

    /**
     * Get the API operation information about the command
     *
     * @return OperationInterface
     */
    public function getOperation();

    /**
     * Execute the command and return the result
     *
     * @return mixed Returns the result of {@see CommandInterface::execute}
     * @throws CommandException if a client has not been associated with the command
     */
    public function execute();

    /**
     * Get the client object that will execute the command
     *
     * @return ClientInterface|null
     */
    public function getClient();

    /**
     * Set the client object that will execute the command
     *
     * @param ClientInterface $client The client object that will execute the command
     *
     * @return self
     */
    public function setClient(ClientInterface $client);

    /**
     * Get the request object associated with the command
     *
     * @return RequestInterface
     * @throws CommandException if the command has not been executed
     */
    public function getRequest();

    /**
     * Get the response object associated with the command
     *
     * @return Response
     * @throws CommandException if the command has not been executed
     */
    public function getResponse();

    /**
     * Get the result of the command
     *
     * @return Response By default, commands return a Response object unless overridden in a subclass
     * @throws CommandException if the command has not been executed
     */
    public function getResult();

    /**
     * Set the result of the command
     *
     * @param mixed $result Result to set
     *
     * @return self
     */
    public function setResult($result);

    /**
     * Returns TRUE if the command has been prepared for executing
     *
     * @return bool
     */
    public function isPrepared();

    /**
     * Returns TRUE if the command has been executed
     *
     * @return bool
     */
    public function isExecuted();

    /**
     * Prepare the command for executing and create a request object.
     *
     * @return RequestInterface Returns the generated request
     * @throws CommandException if a client object has not been set previously or in the prepare()
     */
    public function prepare();

    /**
     * Get the object that manages the request headers that will be set on any outbound requests from the command
     *
     * @return Collection
     */
    public function getRequestHeaders();

    /**
     * Specify a callable to execute when the command completes
     *
     * @param mixed $callable Callable to execute when the command completes. The callable must accept a
     *                        {@see CommandInterface} object as the only argument.
     * @return self
     * @throws InvalidArgumentException
     */
    public function setOnComplete($callable);
}
<?php

namespace Guzzle\Service\Command;

use Guzzle\Common\Event;

/**
 * Event class emitted with the operation.parse_class event
 */
class CreateResponseClassEvent extends Event
{
    /**
     * Set the result of the object creation
     *
     * @param mixed $result Result value to set
     */
    public function setResult($result)
    {
        $this['result'] = $result;
        $this->stopPropagation();
    }

    /**
     * Get the created object
     *
     * @return mixed
     */
    public function getResult()
    {
        return $this['result'];
    }
}
<?php

namespace Guzzle\Service\Command;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\LocationVisitor\Request\RequestVisitorInterface;
use Guzzle\Service\Command\LocationVisitor\VisitorFlyweight;
use Guzzle\Service\Description\OperationInterface;
use Guzzle\Service\Description\Parameter;

/**
 * Default request serializer that transforms command options and operation parameters into a request
 */
class DefaultRequestSerializer implements RequestSerializerInterface
{
    /** @var VisitorFlyweight $factory Visitor factory */
    protected $factory;

    /** @var self */
    protected static $instance;

    /**
     * @return self
     * @codeCoverageIgnore
     */
    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new self(VisitorFlyweight::getInstance());
        }

        return self::$instance;
    }

    /**
     * @param VisitorFlyweight $factory Factory to use when creating visitors
     */
    public function __construct(VisitorFlyweight $factory)
    {
        $this->factory = $factory;
    }

    /**
     * Add a location visitor to the serializer
     *
     * @param string                   $location Location to associate with the visitor
     * @param RequestVisitorInterface  $visitor  Visitor to attach
     *
     * @return self
     */
    public function addVisitor($location, RequestVisitorInterface $visitor)
    {
        $this->factory->addRequestVisitor($location, $visitor);

        return $this;
    }

    public function prepare(CommandInterface $command)
    {
        $request = $this->createRequest($command);
        // Keep an array of visitors found in the operation
        $foundVisitors = array();
        $operation = $command->getOperation();

        // Add arguments to the request using the location attribute
        foreach ($operation->getParams() as $name => $arg) {
            /** @var $arg \Guzzle\Service\Description\Parameter */
            $location = $arg->getLocation();
            // Skip 'uri' locations because they've already been processed
            if ($location && $location != 'uri') {
                // Instantiate visitors as they are detected in the properties
                if (!isset($foundVisitors[$location])) {
                    $foundVisitors[$location] = $this->factory->getRequestVisitor($location);
                }
                // Ensure that a value has been set for this parameter
                $value = $command[$name];
                if ($value !== null) {
                    // Apply the parameter value with the location visitor
                    $foundVisitors[$location]->visit($command, $request, $arg, $value);
                }
            }
        }

        // Serialize additional parameters
        if ($additional = $operation->getAdditionalParameters()) {
            if ($visitor = $this->prepareAdditionalParameters($operation, $command, $request, $additional)) {
                $foundVisitors[$additional->getLocation()] = $visitor;
            }
        }

        // Call the after method on each visitor found in the operation
        foreach ($foundVisitors as $visitor) {
            $visitor->after($command, $request);
        }

        return $request;
    }

    /**
     * Serialize additional parameters
     *
     * @param OperationInterface $operation  Operation that owns the command
     * @param CommandInterface   $command    Command to prepare
     * @param RequestInterface   $request    Request to serialize
     * @param Parameter          $additional Additional parameters
     *
     * @return null|RequestVisitorInterface
     */
    protected function prepareAdditionalParameters(
        OperationInterface $operation,
        CommandInterface $command,
        RequestInterface $request,
        Parameter $additional
    ) {
        if (!($location = $additional->getLocation())) {
            return;
        }

        $visitor = $this->factory->getRequestVisitor($location);
        $hidden = $command[$command::HIDDEN_PARAMS];

        foreach ($command->toArray() as $key => $value) {
            // Ignore values that are null or built-in command options
            if ($value !== null
                && !in_array($key, $hidden)
                && !$operation->hasParam($key)
            ) {
                $additional->setName($key);
                $visitor->visit($command, $request, $additional, $value);
            }
        }

        return $visitor;
    }

    /**
     * Create a request for the command and operation
     *
     * @param CommandInterface $command Command to create a request for
     *
     * @return RequestInterface
     */
    protected function createRequest(CommandInterface $command)
    {
        $operation = $command->getOperation();
        $client = $command->getClient();
        $options = $command[AbstractCommand::REQUEST_OPTIONS] ?: array();

        // If the command does not specify a template, then assume the base URL of the client
        if (!($uri = $operation->getUri())) {
            return $client->createRequest($operation->getHttpMethod(), $client->getBaseUrl(), null, null, $options);
        }

        // Get the path values and use the client config settings
        $variables = array();
        foreach ($operation->getParams() as $name => $arg) {
            if ($arg->getLocation() == 'uri') {
                if (isset($command[$name])) {
                    $variables[$name] = $arg->filter($command[$name]);
                    if (!is_array($variables[$name])) {
                        $variables[$name] = (string) $variables[$name];
                    }
                }
            }
        }

        return $client->createRequest($operation->getHttpMethod(), array($uri, $variables), null, null, $options);
    }
}
<?php

namespace Guzzle\Service\Command;

use Guzzle\Http\Message\Response;

/**
 * Default HTTP response parser used to marshal JSON responses into arrays and XML responses into SimpleXMLElement
 */
class DefaultResponseParser implements ResponseParserInterface
{
    /** @var self */
    protected static $instance;

    /**
     * @return self
     * @codeCoverageIgnore
     */
    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new self;
        }

        return self::$instance;
    }

    public function parse(CommandInterface $command)
    {
        $response = $command->getRequest()->getResponse();

        // Account for hard coded content-type values specified in service descriptions
        if ($contentType = $command['command.expects']) {
            $response->setHeader('Content-Type', $contentType);
        } else {
            $contentType = (string) $response->getHeader('Content-Type');
        }

        return $this->handleParsing($command, $response, $contentType);
    }

    protected function handleParsing(CommandInterface $command, Response $response, $contentType)
    {
        $result = $response;
        if ($result->getBody()) {
            if (stripos($contentType, 'json') !== false) {
                $result = $result->json();
            } elseif (stripos($contentType, 'xml') !== false) {
                $result = $result->xml();
            }
        }

        return $result;
    }
}
<?php

namespace Guzzle\Service\Command\Factory;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Service\ClientInterface;

/**
 * Command factory used when you need to provide aliases to commands
 */
class AliasFactory implements FactoryInterface
{
    /** @var array Associative array mapping command aliases to the aliased command */
    protected $aliases;

    /** @var ClientInterface Client used to retry using aliases */
    protected $client;

    /**
     * @param ClientInterface $client  Client used to retry with the alias
     * @param array           $aliases Associative array mapping aliases to the alias
     */
    public function __construct(ClientInterface $client, array $aliases)
    {
        $this->client = $client;
        $this->aliases = $aliases;
    }

    public function factory($name, array $args = array())
    {
        if (isset($this->aliases[$name])) {
            try {
                return $this->client->getCommand($this->aliases[$name], $args);
            } catch (InvalidArgumentException $e) {
                return null;
            }
        }
    }
}
<?php

namespace Guzzle\Service\Command\Factory;

use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\ClientInterface;

/**
 * Composite factory used by a client object to create command objects utilizing multiple factories
 */
class CompositeFactory implements \IteratorAggregate, \Countable, FactoryInterface
{
    /** @var array Array of command factories */
    protected $factories;

    /**
     * Get the default chain to use with clients
     *
     * @param ClientInterface $client Client to base the chain on
     *
     * @return self
     */
    public static function getDefaultChain(ClientInterface $client)
    {
        $factories = array();
        if ($description = $client->getDescription()) {
            $factories[] = new ServiceDescriptionFactory($description);
        }
        $factories[] = new ConcreteClassFactory($client);

        return new self($factories);
    }

    /**
     * @param array $factories Array of command factories
     */
    public function __construct(array $factories = array())
    {
        $this->factories = $factories;
    }

    /**
     * Add a command factory to the chain
     *
     * @param FactoryInterface        $factory Factory to add
     * @param string|FactoryInterface $before  Insert the new command factory before a command factory class or object
     *                                         matching a class name.
     * @return CompositeFactory
     */
    public function add(FactoryInterface $factory, $before = null)
    {
        $pos = null;

        if ($before) {
            foreach ($this->factories as $i => $f) {
                if ($before instanceof FactoryInterface) {
                    if ($f === $before) {
                        $pos = $i;
                        break;
                    }
                } elseif (is_string($before)) {
                    if ($f instanceof $before) {
                        $pos = $i;
                        break;
                    }
                }
            }
        }

        if ($pos === null) {
            $this->factories[] = $factory;
        } else {
            array_splice($this->factories, $i, 0, array($factory));
        }

        return $this;
    }

    /**
     * Check if the chain contains a specific command factory
     *
     * @param FactoryInterface|string $factory Factory to check
     *
     * @return bool
     */
    public function has($factory)
    {
        return (bool) $this->find($factory);
    }

    /**
     * Remove a specific command factory from the chain
     *
     * @param string|FactoryInterface $factory Factory to remove by name or instance
     *
     * @return CompositeFactory
     */
    public function remove($factory = null)
    {
        if (!($factory instanceof FactoryInterface)) {
            $factory = $this->find($factory);
        }

        $this->factories = array_values(array_filter($this->factories, function($f) use ($factory) {
            return $f !== $factory;
        }));

        return $this;
    }

    /**
     * Get a command factory by class name
     *
     * @param string|FactoryInterface $factory Command factory class or instance
     *
     * @return null|FactoryInterface
     */
    public function find($factory)
    {
        foreach ($this->factories as $f) {
            if ($factory === $f || (is_string($factory) && $f instanceof $factory)) {
                return $f;
            }
        }
    }

    /**
     * Create a command using the associated command factories
     *
     * @param string $name Name of the command
     * @param array  $args Command arguments
     *
     * @return CommandInterface
     */
    public function factory($name, array $args = array())
    {
        foreach ($this->factories as $factory) {
            $command = $factory->factory($name, $args);
            if ($command) {
                return $command;
            }
        }
    }

    public function count()
    {
        return count($this->factories);
    }

    public function getIterator()
    {
        return new \ArrayIterator($this->factories);
    }
}
<?php

namespace Guzzle\Service\Command\Factory;

use Guzzle\Inflection\InflectorInterface;
use Guzzle\Inflection\Inflector;
use Guzzle\Service\ClientInterface;

/**
 * Command factory used to create commands referencing concrete command classes
 */
class ConcreteClassFactory implements FactoryInterface
{
    /** @var ClientInterface */
    protected $client;

    /** @var InflectorInterface */
    protected $inflector;

    /**
     * @param ClientInterface    $client    Client that owns the commands
     * @param InflectorInterface $inflector Inflector used to resolve class names
     */
    public function __construct(ClientInterface $client, InflectorInterface $inflector = null)
    {
        $this->client = $client;
        $this->inflector = $inflector ?: Inflector::getDefault();
    }

    public function factory($name, array $args = array())
    {
        // Determine the class to instantiate based on the namespace of the current client and the default directory
        $prefix = $this->client->getConfig('command.prefix');
        if (!$prefix) {
            // The prefix can be specified in a factory method and is cached
            $prefix = implode('\\', array_slice(explode('\\', get_class($this->client)), 0, -1)) . '\\Command\\';
            $this->client->getConfig()->set('command.prefix', $prefix);
        }

        $class = $prefix . str_replace(' ', '\\', ucwords(str_replace('.', ' ', $this->inflector->camel($name))));

        // Create the concrete command if it exists
        if (class_exists($class)) {
            return new $class($args);
        }
    }
}
<?php

namespace Guzzle\Service\Command\Factory;

use Guzzle\Service\Command\CommandInterface;

/**
 * Interface for creating commands by name
 */
interface FactoryInterface
{
    /**
     * Create a command by name
     *
     * @param string $name Command to create
     * @param array  $args Command arguments
     *
     * @return CommandInterface|null
     */
    public function factory($name, array $args = array());
}
<?php

namespace Guzzle\Service\Command\Factory;

/**
 * Command factory used when explicitly mapping strings to command classes
 */
class MapFactory implements FactoryInterface
{
    /** @var array Associative array mapping command names to classes */
    protected $map;

    /** @param array $map Associative array mapping command names to classes */
    public function __construct(array $map)
    {
        $this->map = $map;
    }

    public function factory($name, array $args = array())
    {
        if (isset($this->map[$name])) {
            $class = $this->map[$name];

            return new $class($args);
        }
    }
}
<?php

namespace Guzzle\Service\Command\Factory;

use Guzzle\Service\Description\ServiceDescriptionInterface;
use Guzzle\Inflection\InflectorInterface;

/**
 * Command factory used to create commands based on service descriptions
 */
class ServiceDescriptionFactory implements FactoryInterface
{
    /** @var ServiceDescriptionInterface */
    protected $description;

    /** @var InflectorInterface */
    protected $inflector;

    /**
     * @param ServiceDescriptionInterface $description Service description
     * @param InflectorInterface          $inflector   Optional inflector to use if the command is not at first found
     */
    public function __construct(ServiceDescriptionInterface $description, InflectorInterface $inflector = null)
    {
        $this->setServiceDescription($description);
        $this->inflector = $inflector;
    }

    /**
     * Change the service description used with the factory
     *
     * @param ServiceDescriptionInterface $description Service description to use
     *
     * @return FactoryInterface
     */
    public function setServiceDescription(ServiceDescriptionInterface $description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Returns the service description
     *
     * @return ServiceDescriptionInterface
     */
    public function getServiceDescription()
    {
        return $this->description;
    }

    public function factory($name, array $args = array())
    {
        $command = $this->description->getOperation($name);

        // If a command wasn't found, then try to uppercase the first letter and try again
        if (!$command) {
            $command = $this->description->getOperation(ucfirst($name));
            // If an inflector was passed, then attempt to get the command using snake_case inflection
            if (!$command && $this->inflector) {
                $command = $this->description->getOperation($this->inflector->snake($name));
            }
        }

        if ($command) {
            $class = $command->getClass();
            return new $class($args, $command, $this->description);
        }
    }
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Service\Command\CommandInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Description\Parameter;

abstract class AbstractRequestVisitor implements RequestVisitorInterface
{
    /**
     * @codeCoverageIgnore
     */
    public function after(CommandInterface $command, RequestInterface $request) {}

    /**
     * @codeCoverageIgnore
     */
    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) {}

    /**
     * Prepare (filter and set desired name for request item) the value for request.
     *
     * @param mixed                                     $value
     * @param \Guzzle\Service\Description\Parameter     $param
     *
     * @return array|mixed
     */
    protected function prepareValue($value, Parameter $param)
    {
        return is_array($value)
            ? $this->resolveRecursively($value, $param)
            : $param->filter($value);
    }

    /**
     * Map nested parameters into the location_key based parameters
     *
     * @param array     $value Value to map
     * @param Parameter $param Parameter that holds information about the current key
     *
     * @return array Returns the mapped array
     */
    protected function resolveRecursively(array $value, Parameter $param)
    {
        foreach ($value as $name => &$v) {
            switch ($param->getType()) {
                case 'object':
                    if ($subParam = $param->getProperty($name)) {
                        $key = $subParam->getWireName();
                        $value[$key] = $this->prepareValue($v, $subParam);
                        if ($name != $key) {
                            unset($value[$name]);
                        }
                    } elseif ($param->getAdditionalProperties() instanceof Parameter) {
                        $v = $this->prepareValue($v, $param->getAdditionalProperties());
                    }
                    break;
                case 'array':
                    if ($items = $param->getItems()) {
                        $v = $this->prepareValue($v, $items);
                    }
                    break;
            }
        }

        return $param->filter($value);
    }
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Http\EntityBody;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Parameter;

/**
 * Visitor used to apply a body to a request
 *
 * This visitor can use a data parameter of 'expect' to control the Expect header. Set the expect data parameter to
 * false to disable the expect header, or set the value to an integer so that the expect 100-continue header is only
 * added if the Content-Length of the entity body is greater than the value.
 */
class BodyVisitor extends AbstractRequestVisitor
{
    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
    {
        $value = $param->filter($value);
        $entityBody = EntityBody::factory($value);
        $request->setBody($entityBody);
        $this->addExpectHeader($request, $entityBody, $param->getData('expect_header'));
        // Add the Content-Encoding header if one is set on the EntityBody
        if ($encoding = $entityBody->getContentEncoding()) {
            $request->setHeader('Content-Encoding', $encoding);
        }
    }

    /**
     * Add the appropriate expect header to a request
     *
     * @param EntityEnclosingRequestInterface $request Request to update
     * @param EntityBodyInterface             $body    Entity body of the request
     * @param string|int                      $expect  Expect header setting
     */
    protected function addExpectHeader(EntityEnclosingRequestInterface $request, EntityBodyInterface $body, $expect)
    {
        // Allow the `expect` data parameter to be set to remove the Expect header from the request
        if ($expect === false) {
            $request->removeHeader('Expect');
        } elseif ($expect !== true) {
            // Default to using a MB as the point in which to start using the expect header
            $expect = $expect ?: 1048576;
            // If the expect_header value is numeric then only add if the size is greater than the cutoff
            if (is_numeric($expect) && $body->getSize()) {
                if ($body->getSize() < $expect) {
                    $request->removeHeader('Expect');
                } else {
                    $request->setHeader('Expect', '100-Continue');
                }
            }
        }
    }
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Parameter;

/**
 * Visitor used to apply a parameter to a header value
 */
class HeaderVisitor extends AbstractRequestVisitor
{
    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
    {
        $value = $param->filter($value);
        if ($param->getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) {
            $this->addPrefixedHeaders($request, $param, $value);
        } else {
            $request->setHeader($param->getWireName(), $value);
        }
    }

    /**
     * Add a prefixed array of headers to the request
     *
     * @param RequestInterface $request Request to update
     * @param Parameter        $param   Parameter object
     * @param array            $value   Header array to add
     *
     * @throws InvalidArgumentException
     */
    protected function addPrefixedHeaders(RequestInterface $request, Parameter $param, $value)
    {
        if (!is_array($value)) {
            throw new InvalidArgumentException('An array of mapped headers expected, but received a single value');
        }
        $prefix = $param->getSentAs();
        foreach ($value as $headerName => $headerValue) {
            $request->setHeader($prefix . $headerName, $headerValue);
        }
    }
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Parameter;

/**
 * Visitor used to apply a parameter to an array that will be serialized as a top level key-value pair in a JSON body
 */
class JsonVisitor extends AbstractRequestVisitor
{
    /** @var bool Whether or not to add a Content-Type header when JSON is found */
    protected $jsonContentType = 'application/json';

    /** @var \SplObjectStorage Data object for persisting JSON data */
    protected $data;

    public function __construct()
    {
        $this->data = new \SplObjectStorage();
    }

    /**
     * Set the Content-Type header to add to the request if JSON is added to the body. This visitor does not add a
     * Content-Type header unless you specify one here.
     *
     * @param string $header Header to set when JSON is added (e.g. application/json)
     *
     * @return self
     */
    public function setContentTypeHeader($header = 'application/json')
    {
        $this->jsonContentType = $header;

        return $this;
    }

    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
    {
        if (isset($this->data[$command])) {
            $json = $this->data[$command];
        } else {
            $json = array();
        }
        $json[$param->getWireName()] = $this->prepareValue($value, $param);
        $this->data[$command] = $json;
    }

    public function after(CommandInterface $command, RequestInterface $request)
    {
        if (isset($this->data[$command])) {
            // Don't overwrite the Content-Type if one is set
            if ($this->jsonContentType && !$request->hasHeader('Content-Type')) {
                $request->setHeader('Content-Type', $this->jsonContentType);
            }

            $request->setBody(json_encode($this->data[$command]));
            unset($this->data[$command]);
        }
    }
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Parameter;

/**
 * Visitor used to apply a parameter to a POST field
 */
class PostFieldVisitor extends AbstractRequestVisitor
{
    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
    {
        $request->setPostField($param->getWireName(), $this->prepareValue($value, $param));
    }
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\PostFileInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Parameter;

/**
 * Visitor used to apply a parameter to a POST file
 */
class PostFileVisitor extends AbstractRequestVisitor
{
    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
    {
        $value = $param->filter($value);
        if ($value instanceof PostFileInterface) {
            $request->addPostFile($value);
        } else {
            $request->addPostFile($param->getWireName(), $value);
        }
    }
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Parameter;

/**
 * Visitor used to apply a parameter to a request's query string
 */
class QueryVisitor extends AbstractRequestVisitor
{
    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
    {
        $request->getQuery()->set($param->getWireName(), $this->prepareValue($value, $param));
    }
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\CommandInterface;

/**
 * Location visitor used to add values to different locations in a request with different behaviors as needed
 */
interface RequestVisitorInterface
{
    /**
     * Called after visiting all parameters
     *
     * @param CommandInterface $command Command being visited
     * @param RequestInterface $request Request being visited
     */
    public function after(CommandInterface $command, RequestInterface $request);

    /**
     * Called once for each parameter being visited that matches the location type
     *
     * @param CommandInterface $command Command being visited
     * @param RequestInterface $request Request being visited
     * @param Parameter        $param   Parameter being visited
     * @param mixed            $value   Value to set
     */
    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value);
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Parameter;

/**
 * Visitor used to change the location in which a response body is saved
 */
class ResponseBodyVisitor extends AbstractRequestVisitor
{
    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
    {
        $request->setResponseBody($value);
    }
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Operation;
use Guzzle\Service\Description\Parameter;

/**
 * Location visitor used to serialize XML bodies
 */
class XmlVisitor extends AbstractRequestVisitor
{
    /** @var \SplObjectStorage Data object for persisting XML data */
    protected $data;

    /** @var bool Content-Type header added when XML is found */
    protected $contentType = 'application/xml';

    public function __construct()
    {
        $this->data = new \SplObjectStorage();
    }

    /**
     * Change the content-type header that is added when XML is found
     *
     * @param string $header Header to set when XML is found
     *
     * @return self
     */
    public function setContentTypeHeader($header)
    {
        $this->contentType = $header;

        return $this;
    }

    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
    {
        $xml = isset($this->data[$command])
            ? $this->data[$command]
            : $this->createRootElement($param->getParent());
        $this->addXml($xml, $param, $value);

        $this->data[$command] = $xml;
    }

    public function after(CommandInterface $command, RequestInterface $request)
    {
        $xml = null;

        // If data was found that needs to be serialized, then do so
        if (isset($this->data[$command])) {
            $xml = $this->finishDocument($this->data[$command]);
            unset($this->data[$command]);
        } else {
            // Check if XML should always be sent for the command
            $operation = $command->getOperation();
            if ($operation->getData('xmlAllowEmpty')) {
                $xmlWriter = $this->createRootElement($operation);
                $xml = $this->finishDocument($xmlWriter);
            }
        }

        if ($xml) {
            // Don't overwrite the Content-Type if one is set
            if ($this->contentType && !$request->hasHeader('Content-Type')) {
                $request->setHeader('Content-Type', $this->contentType);
            }
            $request->setBody($xml);
        }
    }

    /**
     * Create the root XML element to use with a request
     *
     * @param Operation $operation Operation object
     *
     * @return \XMLWriter
     */
    protected function createRootElement(Operation $operation)
    {
        static $defaultRoot = array('name' => 'Request');
        // If no root element was specified, then just wrap the XML in 'Request'
        $root = $operation->getData('xmlRoot') ?: $defaultRoot;
        // Allow the XML declaration to be customized with xmlEncoding
        $encoding = $operation->getData('xmlEncoding');

        $xmlWriter = $this->startDocument($encoding);

        $xmlWriter->startElement($root['name']);
        // Create the wrapping element with no namespaces if no namespaces were present
        if (!empty($root['namespaces'])) {
            // Create the wrapping element with an array of one or more namespaces
            foreach ((array) $root['namespaces'] as $prefix => $uri) {
                $nsLabel = 'xmlns';
                if (!is_numeric($prefix)) {
                    $nsLabel .= ':'.$prefix;
                }
                $xmlWriter->writeAttribute($nsLabel, $uri);
            }
        }
        return $xmlWriter;
    }

    /**
     * Recursively build the XML body
     *
     * @param \XMLWriter $xmlWriter XML to modify
     * @param Parameter  $param     API Parameter
     * @param mixed      $value     Value to add
     */
    protected function addXml(\XMLWriter $xmlWriter, Parameter $param, $value)
    {
        if ($value === null) {
            return;
        }

        $value = $param->filter($value);
        $type = $param->getType();
        $name = $param->getWireName();
        $prefix = null;
        $namespace = $param->getData('xmlNamespace');
        if (false !== strpos($name, ':')) {
            list($prefix, $name) = explode(':', $name, 2);
        }

        if ($type == 'object' || $type == 'array') {
            if (!$param->getData('xmlFlattened')) {
                $xmlWriter->startElementNS(null, $name, $namespace);
            }
            if ($param->getType() == 'array') {
                $this->addXmlArray($xmlWriter, $param, $value);
            } elseif ($param->getType() == 'object') {
                $this->addXmlObject($xmlWriter, $param, $value);
            }
            if (!$param->getData('xmlFlattened')) {
                $xmlWriter->endElement();
            }
            return;
        }
        if ($param->getData('xmlAttribute')) {
            $this->writeAttribute($xmlWriter, $prefix, $name, $namespace, $value);
        } else {
            $this->writeElement($xmlWriter, $prefix, $name, $namespace, $value);
        }
    }

    /**
     * Write an attribute with namespace if used
     *
     * @param  \XMLWriter $xmlWriter XMLWriter instance
     * @param  string     $prefix    Namespace prefix if any
     * @param  string     $name      Attribute name
     * @param  string     $namespace The uri of the namespace
     * @param  string     $value     The attribute content
     */
    protected function writeAttribute($xmlWriter, $prefix, $name, $namespace, $value)
    {
        if (empty($namespace)) {
            $xmlWriter->writeAttribute($name, $value);
        } else {
            $xmlWriter->writeAttributeNS($prefix, $name, $namespace, $value);
        }
    }

    /**
     * Write an element with namespace if used
     *
     * @param  \XMLWriter $xmlWriter XML writer resource
     * @param  string     $prefix    Namespace prefix if any
     * @param  string     $name      Element name
     * @param  string     $namespace The uri of the namespace
     * @param  string     $value     The element content
     */
    protected function writeElement(\XMLWriter $xmlWriter, $prefix, $name, $namespace, $value)
    {
        $xmlWriter->startElementNS($prefix, $name, $namespace);
        if (strpbrk($value, '<>&')) {
            $xmlWriter->writeCData($value);
        } else {
            $xmlWriter->writeRaw($value);
        }
        $xmlWriter->endElement();
    }

    /**
     * Create a new xml writer and start a document
     *
     * @param  string $encoding document encoding
     *
     * @return \XMLWriter the writer resource
     */
    protected function startDocument($encoding)
    {
        $xmlWriter = new \XMLWriter();
        $xmlWriter->openMemory();
        $xmlWriter->startDocument('1.0', $encoding);

        return $xmlWriter;
    }

    /**
     * End the document and return the output
     *
     * @param \XMLWriter $xmlWriter
     *
     * @return \string the writer resource
     */
    protected function finishDocument($xmlWriter)
    {
        $xmlWriter->endDocument();

        return $xmlWriter->outputMemory();
    }

    /**
     * Add an array to the XML
     */
    protected function addXmlArray(\XMLWriter $xmlWriter, Parameter $param, &$value)
    {
        if ($items = $param->getItems()) {
            foreach ($value as $v) {
                $this->addXml($xmlWriter, $items, $v);
            }
        }
    }

    /**
     * Add an object to the XML
     */
    protected function addXmlObject(\XMLWriter $xmlWriter, Parameter $param, &$value)
    {
        $noAttributes = array();
        // add values which have attributes
        foreach ($value as $name => $v) {
            if ($property = $param->getProperty($name)) {
                if ($property->getData('xmlAttribute')) {
                    $this->addXml($xmlWriter, $property, $v);
                } else {
                    $noAttributes[] = array('value' => $v, 'property' => $property);
                }
            }
        }
        // now add values with no attributes
        foreach ($noAttributes as $element) {
            $this->addXml($xmlWriter, $element['property'], $element['value']);
        }
    }
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Response;

use Guzzle\Service\Command\CommandInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;

/**
 * {@inheritdoc}
 * @codeCoverageIgnore
 */
abstract class AbstractResponseVisitor implements ResponseVisitorInterface
{
    public function before(CommandInterface $command, array &$result) {}

    public function after(CommandInterface $command) {}

    public function visit(
        CommandInterface $command,
        Response $response,
        Parameter $param,
        &$value,
        $context =  null
    ) {}
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Response;

use Guzzle\Service\Command\CommandInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;

/**
 * Visitor used to add the body of a response to a particular key
 */
class BodyVisitor extends AbstractResponseVisitor
{
    public function visit(
        CommandInterface $command,
        Response $response,
        Parameter $param,
        &$value,
        $context =  null
    ) {
        $value[$param->getName()] = $param->filter($response->getBody());
    }
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Response;

use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\CommandInterface;

/**
 * Location visitor used to add a particular header of a response to a key in the result
 */
class HeaderVisitor extends AbstractResponseVisitor
{
    public function visit(
        CommandInterface $command,
        Response $response,
        Parameter $param,
        &$value,
        $context =  null
    ) {
        if ($param->getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) {
            $this->processPrefixedHeaders($response, $param, $value);
        } else {
            $value[$param->getName()] = $param->filter((string) $response->getHeader($param->getWireName()));
        }
    }

    /**
     * Process a prefixed header array
     *
     * @param Response  $response Response that contains the headers
     * @param Parameter $param    Parameter object
     * @param array     $value    Value response array to modify
     */
    protected function processPrefixedHeaders(Response $response, Parameter $param, &$value)
    {
        // Grab prefixed headers that should be placed into an array with the prefix stripped
        if ($prefix = $param->getSentAs()) {
            $container = $param->getName();
            $len = strlen($prefix);
            // Find all matching headers and place them into the containing element
            foreach ($response->getHeaders()->toArray() as $key => $header) {
                if (stripos($key, $prefix) === 0) {
                    // Account for multi-value headers
                    $value[$container][substr($key, $len)] = count($header) == 1 ? end($header) : $header;
                }
            }
        }
    }
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Response;

use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\CommandInterface;

/**
 * Location visitor used to marshal JSON response data into a formatted array.
 *
 * Allows top level JSON parameters to be inserted into the result of a command. The top level attributes are grabbed
 * from the response's JSON data using the name value by default. Filters can be applied to parameters as they are
 * traversed. This allows data to be normalized before returning it to users (for example converting timestamps to
 * DateTime objects).
 */
class JsonVisitor extends AbstractResponseVisitor
{
    public function before(CommandInterface $command, array &$result)
    {
        // Ensure that the result of the command is always rooted with the parsed JSON data
        $result = $command->getResponse()->json();
    }

    public function visit(
        CommandInterface $command,
        Response $response,
        Parameter $param,
        &$value,
        $context =  null
    ) {
        $name = $param->getName();
        $key = $param->getWireName();
        if (isset($value[$key])) {
            $this->recursiveProcess($param, $value[$key]);
            if ($key != $name) {
                $value[$name] = $value[$key];
                unset($value[$key]);
            }
        }
    }

    /**
     * Recursively process a parameter while applying filters
     *
     * @param Parameter $param API parameter being validated
     * @param mixed     $value Value to validate and process. The value may change during this process.
     */
    protected function recursiveProcess(Parameter $param, &$value)
    {
        if ($value === null) {
            return;
        }

        if (is_array($value)) {
            $type = $param->getType();
            if ($type == 'array') {
                foreach ($value as &$item) {
                    $this->recursiveProcess($param->getItems(), $item);
                }
            } elseif ($type == 'object' && !isset($value[0])) {
                // On the above line, we ensure that the array is associative and not numerically indexed
                $knownProperties = array();
                if ($properties = $param->getProperties()) {
                    foreach ($properties as $property) {
                        $name = $property->getName();
                        $key = $property->getWireName();
                        $knownProperties[$name] = 1;
                        if (isset($value[$key])) {
                            $this->recursiveProcess($property, $value[$key]);
                            if ($key != $name) {
                                $value[$name] = $value[$key];
                                unset($value[$key]);
                            }
                        }
                    }
                }

                // Remove any unknown and potentially unsafe properties
                if ($param->getAdditionalProperties() === false) {
                    $value = array_intersect_key($value, $knownProperties);
                } elseif (($additional = $param->getAdditionalProperties()) !== true) {
                    // Validate and filter additional properties
                    foreach ($value as &$v) {
                        $this->recursiveProcess($additional, $v);
                    }
                }
            }
        }

        $value = $param->filter($value);
    }
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Response;

use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\CommandInterface;

/**
 * Location visitor used to add the reason phrase of a response to a key in the result
 */
class ReasonPhraseVisitor extends AbstractResponseVisitor
{
    public function visit(
        CommandInterface $command,
        Response $response,
        Parameter $param,
        &$value,
        $context =  null
    ) {
        $value[$param->getName()] = $response->getReasonPhrase();
    }
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Response;

use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\CommandInterface;

/**
 * Location visitor used to parse values out of a response into an associative array
 */
interface ResponseVisitorInterface
{
    /**
     * Called before visiting all parameters. This can be used for seeding the result of a command with default
     * data (e.g. populating with JSON data in the response then adding to the parsed data).
     *
     * @param CommandInterface $command Command being visited
     * @param array            $result  Result value to update if needed (e.g. parsing XML or JSON)
     */
    public function before(CommandInterface $command, array &$result);

    /**
     * Called after visiting all parameters
     *
     * @param CommandInterface $command Command being visited
     */
    public function after(CommandInterface $command);

    /**
     * Called once for each parameter being visited that matches the location type
     *
     * @param CommandInterface $command  Command being visited
     * @param Response         $response Response being visited
     * @param Parameter        $param    Parameter being visited
     * @param mixed            $value    Result associative array value being updated by reference
     * @param mixed            $context  Parsing context
     */
    public function visit(
        CommandInterface $command,
        Response $response,
        Parameter $param,
        &$value,
        $context =  null
    );
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Response;

use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\CommandInterface;

/**
 * Location visitor used to add the status code of a response to a key in the result
 */
class StatusCodeVisitor extends AbstractResponseVisitor
{
    public function visit(
        CommandInterface $command,
        Response $response,
        Parameter $param,
        &$value,
        $context =  null
    ) {
        $value[$param->getName()] = $response->getStatusCode();
    }
}
<?php

namespace Guzzle\Service\Command\LocationVisitor\Response;

use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\CommandInterface;

/**
 * Location visitor used to marshal XML response data into a formatted array
 */
class XmlVisitor extends AbstractResponseVisitor
{
    public function before(CommandInterface $command, array &$result)
    {
        // Set the result of the command to the array conversion of the XML body
        $result = json_decode(json_encode($command->getResponse()->xml()), true);
    }

    public function visit(
        CommandInterface $command,
        Response $response,
        Parameter $param,
        &$value,
        $context =  null
    ) {
        $sentAs = $param->getWireName();
        $name = $param->getName();
        if (isset($value[$sentAs])) {
            $this->recursiveProcess($param, $value[$sentAs]);
            if ($name != $sentAs) {
                $value[$name] = $value[$sentAs];
                unset($value[$sentAs]);
            }
        }
    }

    /**
     * Recursively process a parameter while applying filters
     *
     * @param Parameter $param API parameter being processed
     * @param mixed     $value Value to validate and process. The value may change during this process.
     */
    protected function recursiveProcess(Parameter $param, &$value)
    {
        $type = $param->getType();

        if (!is_array($value)) {
            if ($type == 'array') {
                // Cast to an array if the value was a string, but should be an array
                $this->recursiveProcess($param->getItems(), $value);
                $value = array($value);
            }
        } elseif ($type == 'object') {
            $this->processObject($param, $value);
        } elseif ($type == 'array') {
            $this->processArray($param, $value);
        } elseif ($type == 'string' && gettype($value) == 'array') {
            $value = '';
        }

        if ($value !== null) {
            $value = $param->filter($value);
        }
    }

    /**
     * Process an array
     *
     * @param Parameter $param API parameter being parsed
     * @param mixed     $value Value to process
     */
    protected function processArray(Parameter $param, &$value)
    {
        // Convert the node if it was meant to be an array
        if (!isset($value[0])) {
            // Collections fo nodes are sometimes wrapped in an additional array. For example:
            // <Items><Item><a>1</a></Item><Item><a>2</a></Item></Items> should become:
            // array('Items' => array(array('a' => 1), array('a' => 2))
            // Some nodes are not wrapped. For example: <Foo><a>1</a></Foo><Foo><a>2</a></Foo>
            // should become array('Foo' => array(array('a' => 1), array('a' => 2))
            if ($param->getItems() && isset($value[$param->getItems()->getWireName()])) {
                // Account for the case of a collection wrapping wrapped nodes: Items => Item[]
                $value = $value[$param->getItems()->getWireName()];
                // If the wrapped node only had one value, then make it an array of nodes
                if (!isset($value[0]) || !is_array($value)) {
                    $value = array($value);
                }
            } elseif (!empty($value)) {
                // Account for repeated nodes that must be an array: Foo => Baz, Foo => Baz, but only if the
                // value is set and not empty
                $value = array($value);
            }
        }

        foreach ($value as &$item) {
            $this->recursiveProcess($param->getItems(), $item);
        }
    }

    /**
     * Process an object
     *
     * @param Parameter $param API parameter being parsed
     * @param mixed     $value Value to process
     */
    protected function processObject(Parameter $param, &$value)
    {
        // Ensure that the array is associative and not numerically indexed
        if (!isset($value[0]) && ($properties = $param->getProperties())) {
            $knownProperties = array();
            foreach ($properties as $property) {
                $name = $property->getName();
                $sentAs = $property->getWireName();
                $knownProperties[$name] = 1;
                if ($property->getData('xmlAttribute')) {
                    $this->processXmlAttribute($property, $value);
                } elseif (isset($value[$sentAs])) {
                    $this->recursiveProcess($property, $value[$sentAs]);
                    if ($name != $sentAs) {
                        $value[$name] = $value[$sentAs];
                        unset($value[$sentAs]);
                    }
                }
            }

            // Remove any unknown and potentially unsafe properties
            if ($param->getAdditionalProperties() === false) {
                $value = array_intersect_key($value, $knownProperties);
            }
        }
    }

    /**
     * Process an XML attribute property
     *
     * @param Parameter $property Property to process
     * @param array     $value    Value to process and update
     */
    protected function processXmlAttribute(Parameter $property, array &$value)
    {
        $sentAs = $property->getWireName();
        if (isset($value['@attributes'][$sentAs])) {
            $value[$property->getName()] = $value['@attributes'][$sentAs];
            unset($value['@attributes'][$sentAs]);
            if (empty($value['@attributes'])) {
                unset($value['@attributes']);
            }
        }
    }
}
<?php

namespace Guzzle\Service\Command\LocationVisitor;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Service\Command\LocationVisitor\Request\RequestVisitorInterface;
use Guzzle\Service\Command\LocationVisitor\Response\ResponseVisitorInterface;

/**
 * Flyweight factory used to instantiate request and response visitors
 */
class VisitorFlyweight
{
    /** @var self Singleton instance of self */
    protected static $instance;

    /** @var array Default array of mappings of location names to classes */
    protected static $defaultMappings = array(
        'request.body'          => 'Guzzle\Service\Command\LocationVisitor\Request\BodyVisitor',
        'request.header'        => 'Guzzle\Service\Command\LocationVisitor\Request\HeaderVisitor',
        'request.json'          => 'Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor',
        'request.postField'     => 'Guzzle\Service\Command\LocationVisitor\Request\PostFieldVisitor',
        'request.postFile'      => 'Guzzle\Service\Command\LocationVisitor\Request\PostFileVisitor',
        'request.query'         => 'Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor',
        'request.response_body' => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor',
        'request.responseBody'  => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor',
        'request.xml'           => 'Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor',
        'response.body'         => 'Guzzle\Service\Command\LocationVisitor\Response\BodyVisitor',
        'response.header'       => 'Guzzle\Service\Command\LocationVisitor\Response\HeaderVisitor',
        'response.json'         => 'Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor',
        'response.reasonPhrase' => 'Guzzle\Service\Command\LocationVisitor\Response\ReasonPhraseVisitor',
        'response.statusCode'   => 'Guzzle\Service\Command\LocationVisitor\Response\StatusCodeVisitor',
        'response.xml'          => 'Guzzle\Service\Command\LocationVisitor\Response\XmlVisitor'
    );

    /** @var array Array of mappings of location names to classes */
    protected $mappings;

    /** @var array Cache of instantiated visitors */
    protected $cache = array();

    /**
     * @return self
     * @codeCoverageIgnore
     */
    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * @param array $mappings Array mapping request.name and response.name to location visitor classes. Leave null to
     *                        use the default values.
     */
    public function __construct(array $mappings = null)
    {
        $this->mappings = $mappings === null ? self::$defaultMappings : $mappings;
    }

    /**
     * Get an instance of a request visitor by location name
     *
     * @param string $visitor Visitor name
     *
     * @return RequestVisitorInterface
     */
    public function getRequestVisitor($visitor)
    {
        return $this->getKey('request.' . $visitor);
    }

    /**
     * Get an instance of a response visitor by location name
     *
     * @param string $visitor Visitor name
     *
     * @return ResponseVisitorInterface
     */
    public function getResponseVisitor($visitor)
    {
        return $this->getKey('response.' . $visitor);
    }

    /**
     * Add a response visitor to the factory by name
     *
     * @param string                  $name    Name of the visitor
     * @param RequestVisitorInterface $visitor Visitor to add
     *
     * @return self
     */
    public function addRequestVisitor($name, RequestVisitorInterface $visitor)
    {
        $this->cache['request.' . $name] = $visitor;

        return $this;
    }

    /**
     * Add a response visitor to the factory by name
     *
     * @param string                   $name    Name of the visitor
     * @param ResponseVisitorInterface $visitor Visitor to add
     *
     * @return self
     */
    public function addResponseVisitor($name, ResponseVisitorInterface $visitor)
    {
        $this->cache['response.' . $name] = $visitor;

        return $this;
    }

    /**
     * Get a visitor by key value name
     *
     * @param string $key Key name to retrieve
     *
     * @return mixed
     * @throws InvalidArgumentException
     */
    private function getKey($key)
    {
        if (!isset($this->cache[$key])) {
            if (!isset($this->mappings[$key])) {
                list($type, $name) = explode('.', $key);
                throw new InvalidArgumentException("No {$type} visitor has been mapped for {$name}");
            }
            $this->cache[$key] = new $this->mappings[$key];
        }

        return $this->cache[$key];
    }
}
<?php

namespace Guzzle\Service\Command;

/**
 * A command that creates requests based on {@see Guzzle\Service\Description\OperationInterface} objects, and if the
 * matching operation uses a service description model in the responseClass attribute, then this command will marshal
 * the response into an associative array based on the JSON schema of the model.
 */
class OperationCommand extends AbstractCommand
{
    /** @var RequestSerializerInterface */
    protected $requestSerializer;

    /** @var ResponseParserInterface Response parser */
    protected $responseParser;

    /**
     * Set the response parser used with the command
     *
     * @param ResponseParserInterface $parser Response parser
     *
     * @return self
     */
    public function setResponseParser(ResponseParserInterface $parser)
    {
        $this->responseParser = $parser;

        return $this;
    }

    /**
     * Set the request serializer used with the command
     *
     * @param RequestSerializerInterface $serializer Request serializer
     *
     * @return self
     */
    public function setRequestSerializer(RequestSerializerInterface $serializer)
    {
        $this->requestSerializer = $serializer;

        return $this;
    }

    /**
     * Get the request serializer used with the command
     *
     * @return RequestSerializerInterface
     */
    public function getRequestSerializer()
    {
        if (!$this->requestSerializer) {
            // Use the default request serializer if none was found
            $this->requestSerializer = DefaultRequestSerializer::getInstance();
        }

        return $this->requestSerializer;
    }

    /**
     * Get the response parser used for the operation
     *
     * @return ResponseParserInterface
     */
    public function getResponseParser()
    {
        if (!$this->responseParser) {
            // Use the default response parser if none was found
            $this->responseParser = OperationResponseParser::getInstance();
        }

        return $this->responseParser;
    }

    protected function build()
    {
        // Prepare and serialize the request
        $this->request = $this->getRequestSerializer()->prepare($this);
    }

    protected function process()
    {
        // Do not process the response if 'command.response_processing' is set to 'raw'
        $this->result = $this[self::RESPONSE_PROCESSING] == self::TYPE_RAW
            ? $this->request->getResponse()
            : $this->getResponseParser()->parse($this);
    }
}
<?php

namespace Guzzle\Service\Command;

use Guzzle\Http\Message\Response;
use Guzzle\Service\Command\LocationVisitor\VisitorFlyweight;
use Guzzle\Service\Command\LocationVisitor\Response\ResponseVisitorInterface;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Description\OperationInterface;
use Guzzle\Service\Description\Operation;
use Guzzle\Service\Exception\ResponseClassException;
use Guzzle\Service\Resource\Model;

/**
 * Response parser that attempts to marshal responses into an associative array based on models in a service description
 */
class OperationResponseParser extends DefaultResponseParser
{
    /** @var VisitorFlyweight $factory Visitor factory */
    protected $factory;

    /** @var self */
    protected static $instance;

    /** @var bool */
    private $schemaInModels;

    /**
     * @return self
     * @codeCoverageIgnore
     */
    public static function getInstance()
    {
        if (!static::$instance) {
            static::$instance = new static(VisitorFlyweight::getInstance());
        }

        return static::$instance;
    }

    /**
     * @param VisitorFlyweight $factory        Factory to use when creating visitors
     * @param bool             $schemaInModels Set to true to inject schemas into models
     */
    public function __construct(VisitorFlyweight $factory, $schemaInModels = false)
    {
        $this->factory = $factory;
        $this->schemaInModels = $schemaInModels;
    }

    /**
     * Add a location visitor to the command
     *
     * @param string                   $location Location to associate with the visitor
     * @param ResponseVisitorInterface $visitor  Visitor to attach
     *
     * @return self
     */
    public function addVisitor($location, ResponseVisitorInterface $visitor)
    {
        $this->factory->addResponseVisitor($location, $visitor);

        return $this;
    }

    protected function handleParsing(CommandInterface $command, Response $response, $contentType)
    {
        $operation = $command->getOperation();
        $type = $operation->getResponseType();
        $model = null;

        if ($type == OperationInterface::TYPE_MODEL) {
            $model = $operation->getServiceDescription()->getModel($operation->getResponseClass());
        } elseif ($type == OperationInterface::TYPE_CLASS) {
            return $this->parseClass($command);
        }

        if (!$model) {
            // Return basic processing if the responseType is not model or the model cannot be found
            return parent::handleParsing($command, $response, $contentType);
        } elseif ($command[AbstractCommand::RESPONSE_PROCESSING] != AbstractCommand::TYPE_MODEL) {
            // Returns a model with no visiting if the command response processing is not model
            return new Model(parent::handleParsing($command, $response, $contentType));
        } else {
            // Only inject the schema into the model if "schemaInModel" is true
            return new Model($this->visitResult($model, $command, $response), $this->schemaInModels ? $model : null);
        }
    }

    /**
     * Parse a class object
     *
     * @param CommandInterface $command Command to parse into an object
     *
     * @return mixed
     * @throws ResponseClassException
     */
    protected function parseClass(CommandInterface $command)
    {
        // Emit the operation.parse_class event. If a listener injects a 'result' property, then that will be the result
        $event = new CreateResponseClassEvent(array('command' => $command));
        $command->getClient()->getEventDispatcher()->dispatch('command.parse_response', $event);
        if ($result = $event->getResult()) {
            return $result;
        }

        $className = $command->getOperation()->getResponseClass();
        if (!method_exists($className, 'fromCommand')) {
            throw new ResponseClassException("{$className} must exist and implement a static fromCommand() method");
        }

        return $className::fromCommand($command);
    }

    /**
     * Perform transformations on the result array
     *
     * @param Parameter        $model    Model that defines the structure
     * @param CommandInterface $command  Command that performed the operation
     * @param Response         $response Response received
     *
     * @return array Returns the array of result data
     */
    protected function visitResult(Parameter $model, CommandInterface $command, Response $response)
    {
        $foundVisitors = $result = $knownProps = array();
        $props = $model->getProperties();

        foreach ($props as $schema) {
            if ($location = $schema->getLocation()) {
                // Trigger the before method on the first found visitor of this type
                if (!isset($foundVisitors[$location])) {
                    $foundVisitors[$location] = $this->factory->getResponseVisitor($location);
                    $foundVisitors[$location]->before($command, $result);
                }
            }
        }

        // Visit additional properties when it is an actual schema
        if (($additional = $model->getAdditionalProperties()) instanceof Parameter) {
            $this->visitAdditionalProperties($model, $command, $response, $additional, $result, $foundVisitors);
        }

        // Apply the parameter value with the location visitor
        foreach ($props as $schema) {
            $knownProps[$schema->getName()] = 1;
            if ($location = $schema->getLocation()) {
                $foundVisitors[$location]->visit($command, $response, $schema, $result);
            }
        }

        // Remove any unknown and potentially unsafe top-level properties
        if ($additional === false) {
            $result = array_intersect_key($result, $knownProps);
        }

        // Call the after() method of each found visitor
        foreach ($foundVisitors as $visitor) {
            $visitor->after($command);
        }

        return $result;
    }

    protected function visitAdditionalProperties(
        Parameter $model,
        CommandInterface $command,
        Response $response,
        Parameter $additional,
        &$result,
        array &$foundVisitors
    ) {
        // Only visit when a location is specified
        if ($location = $additional->getLocation()) {
            if (!isset($foundVisitors[$location])) {
                $foundVisitors[$location] = $this->factory->getResponseVisitor($location);
                $foundVisitors[$location]->before($command, $result);
            }
            // Only traverse if an array was parsed from the before() visitors
            if (is_array($result)) {
                // Find each additional property
                foreach (array_keys($result) as $key) {
                    // Check if the model actually knows this property. If so, then it is not additional
                    if (!$model->getProperty($key)) {
                        // Set the name to the key so that we can parse it with each visitor
                        $additional->setName($key);
                        $foundVisitors[$location]->visit($command, $response, $additional, $result);
                    }
                }
                // Reset the additionalProperties name to null
                $additional->setName(null);
            }
        }
    }
}
<?php

namespace Guzzle\Service\Command;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;

/**
 * Translates command options and operation parameters into a request object
 */
interface RequestSerializerInterface
{
    /**
     * Create a request for a command
     *
     * @param CommandInterface $command Command that will own the request
     *
     * @return RequestInterface
     */
    public function prepare(CommandInterface $command);
}
<?php

namespace Guzzle\Service\Command;

/**
 * Interface used to accept a completed OperationCommand and parse the result into a specific response type
 */
interface ResponseClassInterface
{
    /**
     * Create a response model object from a completed command
     *
     * @param OperationCommand $command That serialized the request
     *
     * @return self
     */
    public static function fromCommand(OperationCommand $command);
}
<?php

namespace Guzzle\Service\Command;

/**
 * Parses the HTTP response of a command and sets the appropriate result on a command object
 */
interface ResponseParserInterface
{
    /**
     * Parse the HTTP response received by the command and update the command's result contents
     *
     * @param CommandInterface $command Command to parse and update
     *
     * @return mixed Returns the result to set on the command
     */
    public function parse(CommandInterface $command);
}
<?php

namespace Guzzle\Service;

/**
 * Interface used for loading configuration data (service descriptions, service builder configs, etc)
 *
 * If a loaded configuration data sets includes a top level key containing an 'includes' section, then the data in the
 * file will extend the merged result of all of the included config files.
 */
interface ConfigLoaderInterface
{
    /**
     * Loads configuration data and returns an array of the loaded result
     *
     * @param mixed $config  Data to load (filename or array of data)
     * @param array $options Array of options to use when loading
     *
     * @return mixed
     */
    public function load($config, array $options = array());
}
<?php

namespace Guzzle\Service\Description;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * Data object holding the information of an API command
 */
class Operation implements OperationInterface
{
    /** @var string Default command class to use when none is specified */
    const DEFAULT_COMMAND_CLASS = 'Guzzle\\Service\\Command\\OperationCommand';

    /** @var array Hashmap of properties that can be specified. Represented as a hash to speed up constructor. */
    protected static $properties = array(
        'name' => true, 'httpMethod' => true, 'uri' => true, 'class' => true, 'responseClass' => true,
        'responseType' => true, 'responseNotes' => true, 'notes' => true, 'summary' => true, 'documentationUrl' => true,
        'deprecated' => true, 'data' => true, 'parameters' => true, 'additionalParameters' => true,
        'errorResponses' => true
    );

    /** @var array Parameters */
    protected $parameters = array();

    /** @var Parameter Additional parameters schema */
    protected $additionalParameters;

    /** @var string Name of the command */
    protected $name;

    /** @var string HTTP method */
    protected $httpMethod;

    /** @var string This is a short summary of what the operation does */
    protected $summary;

    /** @var string A longer text field to explain the behavior of the operation. */
    protected $notes;

    /** @var string Reference URL providing more information about the operation */
    protected $documentationUrl;

    /** @var string HTTP URI of the command */
    protected $uri;

    /** @var string Class of the command object */
    protected $class;

    /** @var string This is what is returned from the method */
    protected $responseClass;

    /** @var string Type information about the response */
    protected $responseType;

    /** @var string Information about the response returned by the operation */
    protected $responseNotes;

    /** @var bool Whether or not the command is deprecated */
    protected $deprecated;

    /** @var array Array of errors that could occur when running the command */
    protected $errorResponses;

    /** @var ServiceDescriptionInterface */
    protected $description;

    /** @var array Extra operation information */
    protected $data;

    /**
     * Builds an Operation object using an array of configuration data:
     * - name:               (string) Name of the command
     * - httpMethod:         (string) HTTP method of the operation
     * - uri:                (string) URI template that can create a relative or absolute URL
     * - class:              (string) Concrete class that implements this command
     * - parameters:         (array) Associative array of parameters for the command. {@see Parameter} for information.
     * - summary:            (string) This is a short summary of what the operation does
     * - notes:              (string) A longer text field to explain the behavior of the operation.
     * - documentationUrl:   (string) Reference URL providing more information about the operation
     * - responseClass:      (string) This is what is returned from the method. Can be a primitive, PSR-0 compliant
     *                       class name, or model.
     * - responseNotes:      (string) Information about the response returned by the operation
     * - responseType:       (string) One of 'primitive', 'class', 'model', or 'documentation'. If not specified, this
     *                       value will be automatically inferred based on whether or not there is a model matching the
     *                       name, if a matching PSR-0 compliant class name is found, or set to 'primitive' by default.
     * - deprecated:         (bool) Set to true if this is a deprecated command
     * - errorResponses:     (array) Errors that could occur when executing the command. Array of hashes, each with a
     *                       'code' (the HTTP response code), 'reason' (response reason phrase or description of the
     *                       error), and 'class' (a custom exception class that would be thrown if the error is
     *                       encountered).
     * - data:               (array) Any extra data that might be used to help build or serialize the operation
     * - additionalParameters: (null|array) Parameter schema to use when an option is passed to the operation that is
     *                                      not in the schema
     *
     * @param array                       $config      Array of configuration data
     * @param ServiceDescriptionInterface $description Service description used to resolve models if $ref tags are found
     */
    public function __construct(array $config = array(), ServiceDescriptionInterface $description = null)
    {
        $this->description = $description;

        // Get the intersection of the available properties and properties set on the operation
        foreach (array_intersect_key($config, self::$properties) as $key => $value) {
            $this->{$key} = $value;
        }

        $this->class = $this->class ?: self::DEFAULT_COMMAND_CLASS;
        $this->deprecated = (bool) $this->deprecated;
        $this->errorResponses = $this->errorResponses ?: array();
        $this->data = $this->data ?: array();

        if (!$this->responseClass) {
            $this->responseClass = 'array';
            $this->responseType = 'primitive';
        } elseif ($this->responseType) {
            // Set the response type to perform validation
            $this->setResponseType($this->responseType);
        } else {
            // A response class was set and no response type was set, so guess what the type is
            $this->inferResponseType();
        }

        // Parameters need special handling when adding
        if ($this->parameters) {
            foreach ($this->parameters as $name => $param) {
                if ($param instanceof Parameter) {
                    $param->setName($name)->setParent($this);
                } elseif (is_array($param)) {
                    $param['name'] = $name;
                    $this->addParam(new Parameter($param, $this->description));
                }
            }
        }

        if ($this->additionalParameters) {
            if ($this->additionalParameters instanceof Parameter) {
                $this->additionalParameters->setParent($this);
            } elseif (is_array($this->additionalParameters)) {
                $this->setadditionalParameters(new Parameter($this->additionalParameters, $this->description));
            }
        }
    }

    public function toArray()
    {
        $result = array();
        // Grab valid properties and filter out values that weren't set
        foreach (array_keys(self::$properties) as $check) {
            if ($value = $this->{$check}) {
                $result[$check] = $value;
            }
        }
        // Remove the name property
        unset($result['name']);
        // Parameters need to be converted to arrays
        $result['parameters'] = array();
        foreach ($this->parameters as $key => $param) {
            $result['parameters'][$key] = $param->toArray();
        }
        // Additional parameters need to be cast to an array
        if ($this->additionalParameters instanceof Parameter) {
            $result['additionalParameters'] = $this->additionalParameters->toArray();
        }

        return $result;
    }

    public function getServiceDescription()
    {
        return $this->description;
    }

    public function setServiceDescription(ServiceDescriptionInterface $description)
    {
        $this->description = $description;

        return $this;
    }

    public function getParams()
    {
        return $this->parameters;
    }

    public function getParamNames()
    {
        return array_keys($this->parameters);
    }

    public function hasParam($name)
    {
        return isset($this->parameters[$name]);
    }

    public function getParam($param)
    {
        return isset($this->parameters[$param]) ? $this->parameters[$param] : null;
    }

    /**
     * Add a parameter to the command
     *
     * @param Parameter $param Parameter to add
     *
     * @return self
     */
    public function addParam(Parameter $param)
    {
        $this->parameters[$param->getName()] = $param;
        $param->setParent($this);

        return $this;
    }

    /**
     * Remove a parameter from the command
     *
     * @param string $name Name of the parameter to remove
     *
     * @return self
     */
    public function removeParam($name)
    {
        unset($this->parameters[$name]);

        return $this;
    }

    public function getHttpMethod()
    {
        return $this->httpMethod;
    }

    /**
     * Set the HTTP method of the command
     *
     * @param string $httpMethod Method to set
     *
     * @return self
     */
    public function setHttpMethod($httpMethod)
    {
        $this->httpMethod = $httpMethod;

        return $this;
    }

    public function getClass()
    {
        return $this->class;
    }

    /**
     * Set the concrete class of the command
     *
     * @param string $className Concrete class name
     *
     * @return self
     */
    public function setClass($className)
    {
        $this->class = $className;

        return $this;
    }

    public function getName()
    {
        return $this->name;
    }

    /**
     * Set the name of the command
     *
     * @param string $name Name of the command
     *
     * @return self
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    public function getSummary()
    {
        return $this->summary;
    }

    /**
     * Set a short summary of what the operation does
     *
     * @param string $summary Short summary of the operation
     *
     * @return self
     */
    public function setSummary($summary)
    {
        $this->summary = $summary;

        return $this;
    }

    public function getNotes()
    {
        return $this->notes;
    }

    /**
     * Set a longer text field to explain the behavior of the operation.
     *
     * @param string $notes Notes on the operation
     *
     * @return self
     */
    public function setNotes($notes)
    {
        $this->notes = $notes;

        return $this;
    }

    public function getDocumentationUrl()
    {
        return $this->documentationUrl;
    }

    /**
     * Set the URL pointing to additional documentation on the command
     *
     * @param string $docUrl Documentation URL
     *
     * @return self
     */
    public function setDocumentationUrl($docUrl)
    {
        $this->documentationUrl = $docUrl;

        return $this;
    }

    public function getResponseClass()
    {
        return $this->responseClass;
    }

    /**
     * Set what is returned from the method. Can be a primitive, class name, or model. For example: 'array',
     * 'Guzzle\\Foo\\Baz', or 'MyModelName' (to reference a model by ID).
     *
     * @param string $responseClass Type of response
     *
     * @return self
     */
    public function setResponseClass($responseClass)
    {
        $this->responseClass = $responseClass;
        $this->inferResponseType();

        return $this;
    }

    public function getResponseType()
    {
        return $this->responseType;
    }

    /**
     * Set qualifying information about the responseClass. One of 'primitive', 'class', 'model', or 'documentation'
     *
     * @param string $responseType Response type information
     *
     * @return self
     * @throws InvalidArgumentException
     */
    public function setResponseType($responseType)
    {
        static $types = array(
            self::TYPE_PRIMITIVE => true,
            self::TYPE_CLASS => true,
            self::TYPE_MODEL => true,
            self::TYPE_DOCUMENTATION => true
        );
        if (!isset($types[$responseType])) {
            throw new InvalidArgumentException('responseType must be one of ' . implode(', ', array_keys($types)));
        }

        $this->responseType = $responseType;

        return $this;
    }

    public function getResponseNotes()
    {
        return $this->responseNotes;
    }

    /**
     * Set notes about the response of the operation
     *
     * @param string $notes Response notes
     *
     * @return self
     */
    public function setResponseNotes($notes)
    {
        $this->responseNotes = $notes;

        return $this;
    }

    public function getDeprecated()
    {
        return $this->deprecated;
    }

    /**
     * Set whether or not the command is deprecated
     *
     * @param bool $isDeprecated Set to true to mark as deprecated
     *
     * @return self
     */
    public function setDeprecated($isDeprecated)
    {
        $this->deprecated = $isDeprecated;

        return $this;
    }

    public function getUri()
    {
        return $this->uri;
    }

    /**
     * Set the URI template of the command
     *
     * @param string $uri URI template to set
     *
     * @return self
     */
    public function setUri($uri)
    {
        $this->uri = $uri;

        return $this;
    }

    public function getErrorResponses()
    {
        return $this->errorResponses;
    }

    /**
     * Add an error to the command
     *
     * @param string $code   HTTP response code
     * @param string $reason HTTP response reason phrase or information about the error
     * @param string $class  Exception class associated with the error
     *
     * @return self
     */
    public function addErrorResponse($code, $reason, $class)
    {
        $this->errorResponses[] = array('code' => $code, 'reason' => $reason, 'class' => $class);

        return $this;
    }

    /**
     * Set all of the error responses of the operation
     *
     * @param array $errorResponses Hash of error name to a hash containing a code, reason, class
     *
     * @return self
     */
    public function setErrorResponses(array $errorResponses)
    {
        $this->errorResponses = $errorResponses;

        return $this;
    }

    public function getData($name)
    {
        return isset($this->data[$name]) ? $this->data[$name] : null;
    }

    /**
     * Set a particular data point on the operation
     *
     * @param string $name  Name of the data value
     * @param mixed  $value Value to set
     *
     * @return self
     */
    public function setData($name, $value)
    {
        $this->data[$name] = $value;

        return $this;
    }

    /**
     * Get the additionalParameters of the operation
     *
     * @return Parameter|null
     */
    public function getAdditionalParameters()
    {
        return $this->additionalParameters;
    }

    /**
     * Set the additionalParameters of the operation
     *
     * @param Parameter|null $parameter Parameter to set
     *
     * @return self
     */
    public function setAdditionalParameters($parameter)
    {
        if ($this->additionalParameters = $parameter) {
            $this->additionalParameters->setParent($this);
        }

        return $this;
    }

    /**
     * Infer the response type from the responseClass value
     */
    protected function inferResponseType()
    {
        static $primitives = array('array' => 1, 'boolean' => 1, 'string' => 1, 'integer' => 1, '' => 1);
        if (isset($primitives[$this->responseClass])) {
            $this->responseType = self::TYPE_PRIMITIVE;
        } elseif ($this->description && $this->description->hasModel($this->responseClass)) {
            $this->responseType = self::TYPE_MODEL;
        } else {
            $this->responseType = self::TYPE_CLASS;
        }
    }
}
<?php

namespace Guzzle\Service\Description;

use Guzzle\Common\ToArrayInterface;

/**
 * Interface defining data objects that hold the information of an API operation
 */
interface OperationInterface extends ToArrayInterface
{
    const TYPE_PRIMITIVE = 'primitive';
    const TYPE_CLASS = 'class';
    const TYPE_DOCUMENTATION = 'documentation';
    const TYPE_MODEL = 'model';

    /**
     * Get the service description that the operation belongs to
     *
     * @return ServiceDescriptionInterface|null
     */
    public function getServiceDescription();

    /**
     * Set the service description that the operation belongs to
     *
     * @param ServiceDescriptionInterface $description Service description
     *
     * @return self
     */
    public function setServiceDescription(ServiceDescriptionInterface $description);

    /**
     * Get the params of the operation
     *
     * @return array
     */
    public function getParams();

    /**
     * Returns an array of parameter names
     *
     * @return array
     */
    public function getParamNames();

    /**
     * Check if the operation has a specific parameter by name
     *
     * @param string $name Name of the param
     *
     * @return bool
     */
    public function hasParam($name);

    /**
     * Get a single parameter of the operation
     *
     * @param string $param Parameter to retrieve by name
     *
     * @return Parameter|null
     */
    public function getParam($param);

    /**
     * Get the HTTP method of the operation
     *
     * @return string|null
     */
    public function getHttpMethod();

    /**
     * Get the concrete operation class that implements this operation
     *
     * @return string
     */
    public function getClass();

    /**
     * Get the name of the operation
     *
     * @return string|null
     */
    public function getName();

    /**
     * Get a short summary of what the operation does
     *
     * @return string|null
     */
    public function getSummary();

    /**
     * Get a longer text field to explain the behavior of the operation
     *
     * @return string|null
     */
    public function getNotes();

    /**
     * Get the documentation URL of the operation
     *
     * @return string|null
     */
    public function getDocumentationUrl();

    /**
     * Get what is returned from the method. Can be a primitive, class name, or model. For example, the responseClass
     * could be 'array', which would inherently use a responseType of 'primitive'. Using a class name would set a
     * responseType of 'class'. Specifying a model by ID will use a responseType of 'model'.
     *
     * @return string|null
     */
    public function getResponseClass();

    /**
     * Get information about how the response is unmarshalled: One of 'primitive', 'class', 'model', or 'documentation'
     *
     * @return string
     */
    public function getResponseType();

    /**
     * Get notes about the response of the operation
     *
     * @return string|null
     */
    public function getResponseNotes();

    /**
     * Get whether or not the operation is deprecated
     *
     * @return bool
     */
    public function getDeprecated();

    /**
     * Get the URI that will be merged into the generated request
     *
     * @return string
     */
    public function getUri();

    /**
     * Get the errors that could be encountered when executing the operation
     *
     * @return array
     */
    public function getErrorResponses();

    /**
     * Get extra data from the operation
     *
     * @param string $name Name of the data point to retrieve
     *
     * @return mixed|null
     */
    public function getData($name);
}
<?php

namespace Guzzle\Service\Description;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * API parameter object used with service descriptions
 */
class Parameter
{
    protected $name;
    protected $description;
    protected $serviceDescription;
    protected $type;
    protected $required;
    protected $enum;
    protected $pattern;
    protected $minimum;
    protected $maximum;
    protected $minLength;
    protected $maxLength;
    protected $minItems;
    protected $maxItems;
    protected $default;
    protected $static;
    protected $instanceOf;
    protected $filters;
    protected $location;
    protected $sentAs;
    protected $data;
    protected $properties = array();
    protected $additionalProperties;
    protected $items;
    protected $parent;
    protected $ref;
    protected $format;
    protected $propertiesCache = null;

    /**
     * Create a new Parameter using an associative array of data. The array can contain the following information:
     * - name:          (string) Unique name of the parameter
     * - type:          (string|array) Type of variable (string, number, integer, boolean, object, array, numeric,
     *                  null, any). Types are using for validation and determining the structure of a parameter. You
     *                  can use a union type by providing an array of simple types. If one of the union types matches
     *                  the provided value, then the value is valid.
     * - instanceOf:    (string) When the type is an object, you can specify the class that the object must implement
     * - required:      (bool) Whether or not the parameter is required
     * - default:       (mixed) Default value to use if no value is supplied
     * - static:        (bool) Set to true to specify that the parameter value cannot be changed from the default
     * - description:   (string) Documentation of the parameter
     * - location:      (string) The location of a request used to apply a parameter. Custom locations can be registered
     *                  with a command, but the defaults are uri, query, header, body, json, xml, postField, postFile.
     * - sentAs:        (string) Specifies how the data being modeled is sent over the wire. For example, you may wish
     *                  to include certain headers in a response model that have a normalized casing of FooBar, but the
     *                  actual header is x-foo-bar. In this case, sentAs would be set to x-foo-bar.
     * - filters:       (array) Array of static method names to to run a parameter value through. Each value in the
     *                  array must be a string containing the full class path to a static method or an array of complex
     *                  filter information. You can specify static methods of classes using the full namespace class
     *                  name followed by '::' (e.g. Foo\Bar::baz()). Some filters require arguments in order to properly
     *                  filter a value. For complex filters, use a hash containing a 'method' key pointing to a static
     *                  method, and an 'args' key containing an array of positional arguments to pass to the method.
     *                  Arguments can contain keywords that are replaced when filtering a value: '@value' is replaced
     *                  with the value being validated, '@api' is replaced with the Parameter object.
     * - properties:    When the type is an object, you can specify nested parameters
     * - additionalProperties: (array) This attribute defines a schema for all properties that are not explicitly
     *                  defined in an object type definition. If specified, the value MUST be a schema or a boolean. If
     *                  false is provided, no additional properties are allowed beyond the properties defined in the
     *                  schema. The default value is an empty schema which allows any value for additional properties.
     * - items:         This attribute defines the allowed items in an instance array, and MUST be a schema or an array
     *                  of schemas. The default value is an empty schema which allows any value for items in the
     *                  instance array.
     *                  When this attribute value is a schema and the instance value is an array, then all the items
     *                  in the array MUST be valid according to the schema.
     * - pattern:       When the type is a string, you can specify the regex pattern that a value must match
     * - enum:          When the type is a string, you can specify a list of acceptable values
     * - minItems:      (int) Minimum number of items allowed in an array
     * - maxItems:      (int) Maximum number of items allowed in an array
     * - minLength:     (int) Minimum length of a string
     * - maxLength:     (int) Maximum length of a string
     * - minimum:       (int) Minimum value of an integer
     * - maximum:       (int) Maximum value of an integer
     * - data:          (array) Any additional custom data to use when serializing, validating, etc
     * - format:        (string) Format used to coax a value into the correct format when serializing or unserializing.
     *                  You may specify either an array of filters OR a format, but not both.
     *                  Supported values: date-time, date, time, timestamp, date-time-http
     * - $ref:          (string) String referencing a service description model. The parameter is replaced by the
     *                  schema contained in the model.
     *
     * @param array                       $data        Array of data as seen in service descriptions
     * @param ServiceDescriptionInterface $description Service description used to resolve models if $ref tags are found
     *
     * @throws InvalidArgumentException
     */
    public function __construct(array $data = array(), ServiceDescriptionInterface $description = null)
    {
        if ($description) {
            if (isset($data['$ref'])) {
                if ($model = $description->getModel($data['$ref'])) {
                    $data = $model->toArray() + $data;
                }
            } elseif (isset($data['extends'])) {
                // If this parameter extends from another parameter then start with the actual data
                // union in the parent's data (e.g. actual supersedes parent)
                if ($extends = $description->getModel($data['extends'])) {
                    $data += $extends->toArray();
                }
            }
        }

        // Pull configuration data into the parameter
        foreach ($data as $key => $value) {
            $this->{$key} = $value;
        }

        $this->serviceDescription = $description;
        $this->required = (bool) $this->required;
        $this->data = (array) $this->data;

        if ($this->filters) {
            $this->setFilters((array) $this->filters);
        }

        if ($this->type == 'object' && $this->additionalProperties === null) {
            $this->additionalProperties = true;
        }
    }

    /**
     * Convert the object to an array
     *
     * @return array
     */
    public function toArray()
    {
        static $checks = array('required', 'description', 'static', 'type', 'format', 'instanceOf', 'location', 'sentAs',
            'pattern', 'minimum', 'maximum', 'minItems', 'maxItems', 'minLength', 'maxLength', 'data', 'enum',
            'filters');

        $result = array();

        // Anything that is in the `Items` attribute of an array *must* include it's name if available
        if ($this->parent instanceof self && $this->parent->getType() == 'array' && isset($this->name)) {
            $result['name'] = $this->name;
        }

        foreach ($checks as $c) {
            if ($value = $this->{$c}) {
                $result[$c] = $value;
            }
        }

        if ($this->default !== null) {
            $result['default'] = $this->default;
        }

        if ($this->items !== null) {
            $result['items'] = $this->getItems()->toArray();
        }

        if ($this->additionalProperties !== null) {
            $result['additionalProperties'] = $this->getAdditionalProperties();
            if ($result['additionalProperties'] instanceof self) {
                $result['additionalProperties'] = $result['additionalProperties']->toArray();
            }
        }

        if ($this->type == 'object' && $this->properties) {
            $result['properties'] = array();
            foreach ($this->getProperties() as $name => $property) {
                $result['properties'][$name] = $property->toArray();
            }
        }

        return $result;
    }

    /**
     * Get the default or static value of the command based on a value
     *
     * @param string $value Value that is currently set
     *
     * @return mixed Returns the value, a static value if one is present, or a default value
     */
    public function getValue($value)
    {
        if ($this->static || ($this->default !== null && $value === null)) {
            return $this->default;
        }

        return $value;
    }

    /**
     * Run a value through the filters OR format attribute associated with the parameter
     *
     * @param mixed $value Value to filter
     *
     * @return mixed Returns the filtered value
     */
    public function filter($value)
    {
        // Formats are applied exclusively and supersed filters
        if ($this->format) {
            return SchemaFormatter::format($this->format, $value);
        }

        // Convert Boolean values
        if ($this->type == 'boolean' && !is_bool($value)) {
            $value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
        }

        // Apply filters to the value
        if ($this->filters) {
            foreach ($this->filters as $filter) {
                if (is_array($filter)) {
                    // Convert complex filters that hold value place holders
                    foreach ($filter['args'] as &$data) {
                        if ($data == '@value') {
                            $data = $value;
                        } elseif ($data == '@api') {
                            $data = $this;
                        }
                    }
                    $value = call_user_func_array($filter['method'], $filter['args']);
                } else {
                    $value = call_user_func($filter, $value);
                }
            }
        }

        return $value;
    }

    /**
     * Get the name of the parameter
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Get the key of the parameter, where sentAs will supersede name if it is set
     *
     * @return string
     */
    public function getWireName()
    {
        return $this->sentAs ?: $this->name;
    }

    /**
     * Set the name of the parameter
     *
     * @param string $name Name to set
     *
     * @return self
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get the type(s) of the parameter
     *
     * @return string|array
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Set the type(s) of the parameter
     *
     * @param string|array $type Type of parameter or array of simple types used in a union
     *
     * @return self
     */
    public function setType($type)
    {
        $this->type = $type;

        return $this;
    }

    /**
     * Get if the parameter is required
     *
     * @return bool
     */
    public function getRequired()
    {
        return $this->required;
    }

    /**
     * Set if the parameter is required
     *
     * @param bool $isRequired Whether or not the parameter is required
     *
     * @return self
     */
    public function setRequired($isRequired)
    {
        $this->required = (bool) $isRequired;

        return $this;
    }

    /**
     * Get the default value of the parameter
     *
     * @return string|null
     */
    public function getDefault()
    {
        return $this->default;
    }

    /**
     * Set the default value of the parameter
     *
     * @param string|null $default Default value to set
     *
     * @return self
     */
    public function setDefault($default)
    {
        $this->default = $default;

        return $this;
    }

    /**
     * Get the description of the parameter
     *
     * @return string|null
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Set the description of the parameter
     *
     * @param string $description Description
     *
     * @return self
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Get the minimum acceptable value for an integer
     *
     * @return int|null
     */
    public function getMinimum()
    {
        return $this->minimum;
    }

    /**
     * Set the minimum acceptable value for an integer
     *
     * @param int|null $min Minimum
     *
     * @return self
     */
    public function setMinimum($min)
    {
        $this->minimum = $min;

        return $this;
    }

    /**
     * Get the maximum acceptable value for an integer
     *
     * @return int|null
     */
    public function getMaximum()
    {
        return $this->maximum;
    }

    /**
     * Set the maximum acceptable value for an integer
     *
     * @param int $max Maximum
     *
     * @return self
     */
    public function setMaximum($max)
    {
        $this->maximum = $max;

        return $this;
    }

    /**
     * Get the minimum allowed length of a string value
     *
     * @return int
     */
    public function getMinLength()
    {
        return $this->minLength;
    }

    /**
     * Set the minimum allowed length of a string value
     *
     * @param int|null $min Minimum
     *
     * @return self
     */
    public function setMinLength($min)
    {
        $this->minLength = $min;

        return $this;
    }

    /**
     * Get the maximum allowed length of a string value
     *
     * @return int|null
     */
    public function getMaxLength()
    {
        return $this->maxLength;
    }

    /**
     * Set the maximum allowed length of a string value
     *
     * @param int $max Maximum length
     *
     * @return self
     */
    public function setMaxLength($max)
    {
        $this->maxLength = $max;

        return $this;
    }

    /**
     * Get the maximum allowed number of items in an array value
     *
     * @return int|null
     */
    public function getMaxItems()
    {
        return $this->maxItems;
    }

    /**
     * Set the maximum allowed number of items in an array value
     *
     * @param int $max Maximum
     *
     * @return self
     */
    public function setMaxItems($max)
    {
        $this->maxItems = $max;

        return $this;
    }

    /**
     * Get the minimum allowed number of items in an array value
     *
     * @return int
     */
    public function getMinItems()
    {
        return $this->minItems;
    }

    /**
     * Set the minimum allowed number of items in an array value
     *
     * @param int|null $min Minimum
     *
     * @return self
     */
    public function setMinItems($min)
    {
        $this->minItems = $min;

        return $this;
    }

    /**
     * Get the location of the parameter
     *
     * @return string|null
     */
    public function getLocation()
    {
        return $this->location;
    }

    /**
     * Set the location of the parameter
     *
     * @param string|null $location Location of the parameter
     *
     * @return self
     */
    public function setLocation($location)
    {
        $this->location = $location;

        return $this;
    }

    /**
     * Get the sentAs attribute of the parameter that used with locations to sentAs an attribute when it is being
     * applied to a location.
     *
     * @return string|null
     */
    public function getSentAs()
    {
        return $this->sentAs;
    }

    /**
     * Set the sentAs attribute
     *
     * @param string|null $name Name of the value as it is sent over the wire
     *
     * @return self
     */
    public function setSentAs($name)
    {
        $this->sentAs = $name;

        return $this;
    }

    /**
     * Retrieve a known property from the parameter by name or a data property by name. When not specific name value
     * is specified, all data properties will be returned.
     *
     * @param string|null $name Specify a particular property name to retrieve
     *
     * @return array|mixed|null
     */
    public function getData($name = null)
    {
        if (!$name) {
            return $this->data;
        }

        if (isset($this->data[$name])) {
            return $this->data[$name];
        } elseif (isset($this->{$name})) {
            return $this->{$name};
        }

        return null;
    }

    /**
     * Set the extra data properties of the parameter or set a specific extra property
     *
     * @param string|array|null $nameOrData The name of a specific extra to set or an array of extras to set
     * @param mixed|null        $data       When setting a specific extra property, specify the data to set for it
     *
     * @return self
     */
    public function setData($nameOrData, $data = null)
    {
        if (is_array($nameOrData)) {
            $this->data = $nameOrData;
        } else {
            $this->data[$nameOrData] = $data;
        }

        return $this;
    }

    /**
     * Get whether or not the default value can be changed
     *
     * @return mixed|null
     */
    public function getStatic()
    {
        return $this->static;
    }

    /**
     * Set to true if the default value cannot be changed
     *
     * @param bool $static True or false
     *
     * @return self
     */
    public function setStatic($static)
    {
        $this->static = (bool) $static;

        return $this;
    }

    /**
     * Get an array of filters used by the parameter
     *
     * @return array
     */
    public function getFilters()
    {
        return $this->filters ?: array();
    }

    /**
     * Set the array of filters used by the parameter
     *
     * @param array $filters Array of functions to use as filters
     *
     * @return self
     */
    public function setFilters(array $filters)
    {
        $this->filters = array();
        foreach ($filters as $filter) {
            $this->addFilter($filter);
        }

        return $this;
    }

    /**
     * Add a filter to the parameter
     *
     * @param string|array $filter Method to filter the value through
     *
     * @return self
     * @throws InvalidArgumentException
     */
    public function addFilter($filter)
    {
        if (is_array($filter)) {
            if (!isset($filter['method'])) {
                throw new InvalidArgumentException('A [method] value must be specified for each complex filter');
            }
        }

        if (!$this->filters) {
            $this->filters = array($filter);
        } else {
            $this->filters[] = $filter;
        }

        return $this;
    }

    /**
     * Get the parent object (an {@see OperationInterface} or {@see Parameter}
     *
     * @return OperationInterface|Parameter|null
     */
    public function getParent()
    {
        return $this->parent;
    }

    /**
     * Set the parent object of the parameter
     *
     * @param OperationInterface|Parameter|null $parent Parent container of the parameter
     *
     * @return self
     */
    public function setParent($parent)
    {
        $this->parent = $parent;

        return $this;
    }

    /**
     * Get the properties of the parameter
     *
     * @return array
     */
    public function getProperties()
    {
        if (!$this->propertiesCache) {
            $this->propertiesCache = array();
            foreach (array_keys($this->properties) as $name) {
                $this->propertiesCache[$name] = $this->getProperty($name);
            }
        }

        return $this->propertiesCache;
    }

    /**
     * Get a specific property from the parameter
     *
     * @param string $name Name of the property to retrieve
     *
     * @return null|Parameter
     */
    public function getProperty($name)
    {
        if (!isset($this->properties[$name])) {
            return null;
        }

        if (!($this->properties[$name] instanceof self)) {
            $this->properties[$name]['name'] = $name;
            $this->properties[$name] = new static($this->properties[$name], $this->serviceDescription);
            $this->properties[$name]->setParent($this);
        }

        return $this->properties[$name];
    }

    /**
     * Remove a property from the parameter
     *
     * @param string $name Name of the property to remove
     *
     * @return self
     */
    public function removeProperty($name)
    {
        unset($this->properties[$name]);
        $this->propertiesCache = null;

        return $this;
    }

    /**
     * Add a property to the parameter
     *
     * @param Parameter $property Properties to set
     *
     * @return self
     */
    public function addProperty(Parameter $property)
    {
        $this->properties[$property->getName()] = $property;
        $property->setParent($this);
        $this->propertiesCache = null;

        return $this;
    }

    /**
     * Get the additionalProperties value of the parameter
     *
     * @return bool|Parameter|null
     */
    public function getAdditionalProperties()
    {
        if (is_array($this->additionalProperties)) {
            $this->additionalProperties = new static($this->additionalProperties, $this->serviceDescription);
            $this->additionalProperties->setParent($this);
        }

        return $this->additionalProperties;
    }

    /**
     * Set the additionalProperties value of the parameter
     *
     * @param bool|Parameter|null $additional Boolean to allow any, an Parameter to specify a schema, or false to disallow
     *
     * @return self
     */
    public function setAdditionalProperties($additional)
    {
        $this->additionalProperties = $additional;

        return $this;
    }

    /**
     * Set the items data of the parameter
     *
     * @param Parameter|null $items Items to set
     *
     * @return self
     */
    public function setItems(Parameter $items = null)
    {
        if ($this->items = $items) {
            $this->items->setParent($this);
        }

        return $this;
    }

    /**
     * Get the item data of the parameter
     *
     * @return Parameter|null
     */
    public function getItems()
    {
        if (is_array($this->items)) {
            $this->items = new static($this->items, $this->serviceDescription);
            $this->items->setParent($this);
        }

        return $this->items;
    }

    /**
     * Get the class that the parameter must implement
     *
     * @return null|string
     */
    public function getInstanceOf()
    {
        return $this->instanceOf;
    }

    /**
     * Set the class that the parameter must be an instance of
     *
     * @param string|null $instanceOf Class or interface name
     *
     * @return self
     */
    public function setInstanceOf($instanceOf)
    {
        $this->instanceOf = $instanceOf;

        return $this;
    }

    /**
     * Get the enum of strings that are valid for the parameter
     *
     * @return array|null
     */
    public function getEnum()
    {
        return $this->enum;
    }

    /**
     * Set the enum of strings that are valid for the parameter
     *
     * @param array|null $enum Array of strings or null
     *
     * @return self
     */
    public function setEnum(array $enum = null)
    {
        $this->enum = $enum;

        return $this;
    }

    /**
     * Get the regex pattern that must match a value when the value is a string
     *
     * @return string
     */
    public function getPattern()
    {
        return $this->pattern;
    }

    /**
     * Set the regex pattern that must match a value when the value is a string
     *
     * @param string $pattern Regex pattern
     *
     * @return self
     */
    public function setPattern($pattern)
    {
        $this->pattern = $pattern;

        return $this;
    }

    /**
     * Get the format attribute of the schema
     *
     * @return string
     */
    public function getFormat()
    {
        return $this->format;
    }

    /**
     * Set the format attribute of the schema
     *
     * @param string $format Format to set (e.g. date, date-time, timestamp, time, date-time-http)
     *
     * @return self
     */
    public function setFormat($format)
    {
        $this->format = $format;

        return $this;
    }
}
<?php

namespace Guzzle\Service\Description;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * JSON Schema formatter class
 */
class SchemaFormatter
{
    /** @var \DateTimeZone */
    protected static $utcTimeZone;

    /**
     * Format a value by a registered format name
     *
     * @param string $format Registered format used to format the value
     * @param mixed  $value  Value being formatted
     *
     * @return mixed
     */
    public static function format($format, $value)
    {
        switch ($format) {
            case 'date-time':
                return self::formatDateTime($value);
            case 'date-time-http':
                return self::formatDateTimeHttp($value);
            case 'date':
                return self::formatDate($value);
            case 'time':
                return self::formatTime($value);
            case 'timestamp':
                return self::formatTimestamp($value);
            case 'boolean-string':
                return self::formatBooleanAsString($value);
            default:
                return $value;
        }
    }

    /**
     * Create a ISO 8601 (YYYY-MM-DDThh:mm:ssZ) formatted date time value in UTC time
     *
     * @param string|integer|\DateTime $value Date time value
     *
     * @return string
     */
    public static function formatDateTime($value)
    {
        return self::dateFormatter($value, 'Y-m-d\TH:i:s\Z');
    }

    /**
     * Create an HTTP date (RFC 1123 / RFC 822) formatted UTC date-time string
     *
     * @param string|integer|\DateTime $value Date time value
     *
     * @return string
     */
    public static function formatDateTimeHttp($value)
    {
        return self::dateFormatter($value, 'D, d M Y H:i:s \G\M\T');
    }

    /**
     * Create a YYYY-MM-DD formatted string
     *
     * @param string|integer|\DateTime $value Date time value
     *
     * @return string
     */
    public static function formatDate($value)
    {
        return self::dateFormatter($value, 'Y-m-d');
    }

    /**
     * Create a hh:mm:ss formatted string
     *
     * @param string|integer|\DateTime $value Date time value
     *
     * @return string
     */
    public static function formatTime($value)
    {
        return self::dateFormatter($value, 'H:i:s');
    }

    /**
     * Formats a boolean value as a string
     *
     * @param string|integer|bool $value Value to convert to a boolean 'true' / 'false' value
     *
     * @return string
     */
    public static function formatBooleanAsString($value)
    {
        return filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 'true' : 'false';
    }

    /**
     * Return a UNIX timestamp in the UTC timezone
     *
     * @param string|integer|\DateTime $value Time value
     *
     * @return int
     */
    public static function formatTimestamp($value)
    {
        return (int) self::dateFormatter($value, 'U');
    }

    /**
     * Get a UTC DateTimeZone object
     *
     * @return \DateTimeZone
     */
    protected static function getUtcTimeZone()
    {
        // @codeCoverageIgnoreStart
        if (!self::$utcTimeZone) {
            self::$utcTimeZone = new \DateTimeZone('UTC');
        }
        // @codeCoverageIgnoreEnd

        return self::$utcTimeZone;
    }

    /**
     * Perform the actual DateTime formatting
     *
     * @param int|string|\DateTime $dateTime Date time value
     * @param string               $format   Format of the result
     *
     * @return string
     * @throws InvalidArgumentException
     */
    protected static function dateFormatter($dateTime, $format)
    {
        if (is_numeric($dateTime)) {
            return gmdate($format, (int) $dateTime);
        }

        if (is_string($dateTime)) {
            $dateTime = new \DateTime($dateTime);
        }

        if ($dateTime instanceof \DateTime) {
            return $dateTime->setTimezone(self::getUtcTimeZone())->format($format);
        }

        throw new InvalidArgumentException('Date/Time values must be either a string, integer, or DateTime object');
    }
}
<?php

namespace Guzzle\Service\Description;

use Guzzle\Common\ToArrayInterface;

/**
 * Default parameter validator
 */
class SchemaValidator implements ValidatorInterface
{
    /** @var self Cache instance of the object */
    protected static $instance;

    /** @var bool Whether or not integers are converted to strings when an integer is received for a string input */
    protected $castIntegerToStringType;

    /** @var array Errors encountered while validating */
    protected $errors;

    /**
     * @return self
     * @codeCoverageIgnore
     */
    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * @param bool $castIntegerToStringType Set to true to convert integers into strings when a required type is a
     *                                      string and the input value is an integer. Defaults to true.
     */
    public function __construct($castIntegerToStringType = true)
    {
        $this->castIntegerToStringType = $castIntegerToStringType;
    }

    public function validate(Parameter $param, &$value)
    {
        $this->errors = array();
        $this->recursiveProcess($param, $value);

        if (empty($this->errors)) {
            return true;
        } else {
            sort($this->errors);
            return false;
        }
    }

    /**
     * Get the errors encountered while validating
     *
     * @return array
     */
    public function getErrors()
    {
        return $this->errors ?: array();
    }

    /**
     * Recursively validate a parameter
     *
     * @param Parameter $param  API parameter being validated
     * @param mixed     $value  Value to validate and validate. The value may change during this validate.
     * @param string    $path   Current validation path (used for error reporting)
     * @param int       $depth  Current depth in the validation validate
     *
     * @return bool Returns true if valid, or false if invalid
     */
    protected function recursiveProcess(Parameter $param, &$value, $path = '', $depth = 0)
    {
        // Update the value by adding default or static values
        $value = $param->getValue($value);

        $required = $param->getRequired();
        // if the value is null and the parameter is not required or is static, then skip any further recursion
        if ((null === $value && !$required) || $param->getStatic()) {
            return true;
        }

        $type = $param->getType();
        // Attempt to limit the number of times is_array is called by tracking if the value is an array
        $valueIsArray = is_array($value);
        // If a name is set then update the path so that validation messages are more helpful
        if ($name = $param->getName()) {
            $path .= "[{$name}]";
        }

        if ($type == 'object') {

            // Objects are either associative arrays, ToArrayInterface, or some other object
            if ($param->getInstanceOf()) {
                $instance = $param->getInstanceOf();
                if (!($value instanceof $instance)) {
                    $this->errors[] = "{$path} must be an instance of {$instance}";
                    return false;
                }
            }

            // Determine whether or not this "value" has properties and should be traversed
            $traverse = $temporaryValue = false;

            // Convert the value to an array
            if (!$valueIsArray && $value instanceof ToArrayInterface) {
                $value = $value->toArray();
            }

            if ($valueIsArray) {
                // Ensure that the array is associative and not numerically indexed
                if (isset($value[0])) {
                    $this->errors[] = "{$path} must be an array of properties. Got a numerically indexed array.";
                    return false;
                }
                $traverse = true;
            } elseif ($value === null) {
                // Attempt to let the contents be built up by default values if possible
                $value = array();
                $temporaryValue = $valueIsArray = $traverse = true;
            }

            if ($traverse) {

                if ($properties = $param->getProperties()) {
                    // if properties were found, the validate each property of the value
                    foreach ($properties as $property) {
                        $name = $property->getName();
                        if (isset($value[$name])) {
                            $this->recursiveProcess($property, $value[$name], $path, $depth + 1);
                        } else {
                            $current = null;
                            $this->recursiveProcess($property, $current, $path, $depth + 1);
                            // Only set the value if it was populated with something
                            if (null !== $current) {
                                $value[$name] = $current;
                            }
                        }
                    }
                }

                $additional = $param->getAdditionalProperties();
                if ($additional !== true) {
                    // If additional properties were found, then validate each against the additionalProperties attr.
                    $keys = array_keys($value);
                    // Determine the keys that were specified that were not listed in the properties of the schema
                    $diff = array_diff($keys, array_keys($properties));
                    if (!empty($diff)) {
                        // Determine which keys are not in the properties
                        if ($additional instanceOf Parameter) {
                            foreach ($diff as $key) {
                                $this->recursiveProcess($additional, $value[$key], "{$path}[{$key}]", $depth);
                            }
                        } else {
                            // if additionalProperties is set to false and there are additionalProperties in the values, then fail
                            foreach ($diff as $prop) {
                                $this->errors[] = sprintf('%s[%s] is not an allowed property', $path, $prop);
                            }
                        }
                    }
                }

                // A temporary value will be used to traverse elements that have no corresponding input value.
                // This allows nested required parameters with default values to bubble up into the input.
                // Here we check if we used a temp value and nothing bubbled up, then we need to remote the value.
                if ($temporaryValue && empty($value)) {
                    $value = null;
                    $valueIsArray = false;
                }
            }

        } elseif ($type == 'array' && $valueIsArray && $param->getItems()) {
            foreach ($value as $i => &$item) {
                // Validate each item in an array against the items attribute of the schema
                $this->recursiveProcess($param->getItems(), $item, $path . "[{$i}]", $depth + 1);
            }
        }

        // If the value is required and the type is not null, then there is an error if the value is not set
        if ($required && $value === null && $type != 'null') {
            $message = "{$path} is " . ($param->getType() ? ('a required ' . implode(' or ', (array) $param->getType())) : 'required');
            if ($param->getDescription()) {
                $message .= ': ' . $param->getDescription();
            }
            $this->errors[] = $message;
            return false;
        }

        // Validate that the type is correct. If the type is string but an integer was passed, the class can be
        // instructed to cast the integer to a string to pass validation. This is the default behavior.
        if ($type && (!$type = $this->determineType($type, $value))) {
            if ($this->castIntegerToStringType && $param->getType() == 'string' && is_integer($value)) {
                $value = (string) $value;
            } else {
                $this->errors[] = "{$path} must be of type " . implode(' or ', (array) $param->getType());
            }
        }

        // Perform type specific validation for strings, arrays, and integers
        if ($type == 'string') {

            // Strings can have enums which are a list of predefined values
            if (($enum = $param->getEnum()) && !in_array($value, $enum)) {
                $this->errors[] = "{$path} must be one of " . implode(' or ', array_map(function ($s) {
                    return '"' . addslashes($s) . '"';
                }, $enum));
            }
            // Strings can have a regex pattern that the value must match
            if (($pattern  = $param->getPattern()) && !preg_match($pattern, $value)) {
                $this->errors[] = "{$path} must match the following regular expression: {$pattern}";
            }

            $strLen = null;
            if ($min = $param->getMinLength()) {
                $strLen = strlen($value);
                if ($strLen < $min) {
                    $this->errors[] = "{$path} length must be greater than or equal to {$min}";
                }
            }
            if ($max = $param->getMaxLength()) {
                if (($strLen ?: strlen($value)) > $max) {
                    $this->errors[] = "{$path} length must be less than or equal to {$max}";
                }
            }

        } elseif ($type == 'array') {

            $size = null;
            if ($min = $param->getMinItems()) {
                $size = count($value);
                if ($size < $min) {
                    $this->errors[] = "{$path} must contain {$min} or more elements";
                }
            }
            if ($max = $param->getMaxItems()) {
                if (($size ?: count($value)) > $max) {
                    $this->errors[] = "{$path} must contain {$max} or fewer elements";
                }
            }

        } elseif ($type == 'integer' || $type == 'number' || $type == 'numeric') {
            if (($min = $param->getMinimum()) && $value < $min) {
                $this->errors[] = "{$path} must be greater than or equal to {$min}";
            }
            if (($max = $param->getMaximum()) && $value > $max) {
                $this->errors[] = "{$path} must be less than or equal to {$max}";
            }
        }

        return empty($this->errors);
    }

    /**
     * From the allowable types, determine the type that the variable matches
     *
     * @param string $type  Parameter type
     * @param mixed  $value Value to determine the type
     *
     * @return string|bool Returns the matching type on
     */
    protected function determineType($type, $value)
    {
        foreach ((array) $type as $t) {
            if ($t == 'string' && (is_string($value) || (is_object($value) && method_exists($value, '__toString')))) {
                return 'string';
            } elseif ($t == 'object' && (is_array($value) || is_object($value))) {
                return 'object';
            } elseif ($t == 'array' && is_array($value)) {
                return 'array';
            } elseif ($t == 'integer' && is_integer($value)) {
                return 'integer';
            } elseif ($t == 'boolean' && is_bool($value)) {
                return 'boolean';
            } elseif ($t == 'number' && is_numeric($value)) {
                return 'number';
            } elseif ($t == 'numeric' && is_numeric($value)) {
                return 'numeric';
            } elseif ($t == 'null' && !$value) {
                return 'null';
            } elseif ($t == 'any') {
                return 'any';
            }
        }

        return false;
    }
}
<?php

namespace Guzzle\Service\Description;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\ToArrayInterface;

/**
 * A ServiceDescription stores service information based on a service document
 */
class ServiceDescription implements ServiceDescriptionInterface, ToArrayInterface
{
    /** @var array Array of {@see OperationInterface} objects */
    protected $operations = array();

    /** @var array Array of API models */
    protected $models = array();

    /** @var string Name of the API */
    protected $name;

    /** @var string API version */
    protected $apiVersion;

    /** @var string Summary of the API */
    protected $description;

    /** @var array Any extra API data */
    protected $extraData = array();

    /** @var ServiceDescriptionLoader Factory used in factory method */
    protected static $descriptionLoader;

    /** @var string baseUrl/basePath */
    protected $baseUrl;

    /**
     * {@inheritdoc}
     * @param string|array $config  File to build or array of operation information
     * @param array        $options Service description factory options
     *
     * @return self
     */
    public static function factory($config, array $options = array())
    {
        // @codeCoverageIgnoreStart
        if (!self::$descriptionLoader) {
            self::$descriptionLoader = new ServiceDescriptionLoader();
        }
        // @codeCoverageIgnoreEnd

        return self::$descriptionLoader->load($config, $options);
    }

    /**
     * @param array $config Array of configuration data
     */
    public function __construct(array $config = array())
    {
        $this->fromArray($config);
    }

    public function serialize()
    {
        return json_encode($this->toArray());
    }

    public function unserialize($json)
    {
        $this->operations = array();
        $this->fromArray(json_decode($json, true));
    }

    public function toArray()
    {
        $result = array(
            'name'        => $this->name,
            'apiVersion'  => $this->apiVersion,
            'baseUrl'     => $this->baseUrl,
            'description' => $this->description
        ) + $this->extraData;
        $result['operations'] = array();
        foreach ($this->getOperations() as $name => $operation) {
            $result['operations'][$operation->getName() ?: $name] = $operation->toArray();
        }
        if (!empty($this->models)) {
            $result['models'] = array();
            foreach ($this->models as $id => $model) {
                $result['models'][$id] = $model instanceof Parameter ? $model->toArray(): $model;
            }
        }

        return array_filter($result);
    }

    public function getBaseUrl()
    {
        return $this->baseUrl;
    }

    /**
     * Set the baseUrl of the description
     *
     * @param string $baseUrl Base URL of each operation
     *
     * @return self
     */
    public function setBaseUrl($baseUrl)
    {
        $this->baseUrl = $baseUrl;

        return $this;
    }

    public function getOperations()
    {
        foreach (array_keys($this->operations) as $name) {
            $this->getOperation($name);
        }

        return $this->operations;
    }

    public function hasOperation($name)
    {
        return isset($this->operations[$name]);
    }

    public function getOperation($name)
    {
        // Lazily retrieve and build operations
        if (!isset($this->operations[$name])) {
            return null;
        }

        if (!($this->operations[$name] instanceof Operation)) {
            $this->operations[$name] = new Operation($this->operations[$name], $this);
        }

        return $this->operations[$name];
    }

    /**
     * Add a operation to the service description
     *
     * @param OperationInterface $operation Operation to add
     *
     * @return self
     */
    public function addOperation(OperationInterface $operation)
    {
        $this->operations[$operation->getName()] = $operation->setServiceDescription($this);

        return $this;
    }

    public function getModel($id)
    {
        if (!isset($this->models[$id])) {
            return null;
        }

        if (!($this->models[$id] instanceof Parameter)) {
            $this->models[$id] = new Parameter($this->models[$id] + array('name' => $id), $this);
        }

        return $this->models[$id];
    }

    public function getModels()
    {
        // Ensure all models are converted into parameter objects
        foreach (array_keys($this->models) as $id) {
            $this->getModel($id);
        }

        return $this->models;
    }

    public function hasModel($id)
    {
        return isset($this->models[$id]);
    }

    /**
     * Add a model to the service description
     *
     * @param Parameter $model Model to add
     *
     * @return self
     */
    public function addModel(Parameter $model)
    {
        $this->models[$model->getName()] = $model;

        return $this;
    }

    public function getApiVersion()
    {
        return $this->apiVersion;
    }

    public function getName()
    {
        return $this->name;
    }

    public function getDescription()
    {
        return $this->description;
    }

    public function getData($key)
    {
        return isset($this->extraData[$key]) ? $this->extraData[$key] : null;
    }

    public function setData($key, $value)
    {
        $this->extraData[$key] = $value;

        return $this;
    }

    /**
     * Initialize the state from an array
     *
     * @param array $config Configuration data
     * @throws InvalidArgumentException
     */
    protected function fromArray(array $config)
    {
        // Keep a list of default keys used in service descriptions that is later used to determine extra data keys
        static $defaultKeys = array('name', 'models', 'apiVersion', 'baseUrl', 'description');
        // Pull in the default configuration values
        foreach ($defaultKeys as $key) {
            if (isset($config[$key])) {
                $this->{$key} = $config[$key];
            }
        }

        // Account for the Swagger name for Guzzle's baseUrl
        if (isset($config['basePath'])) {
            $this->baseUrl = $config['basePath'];
        }

        // Ensure that the models and operations properties are always arrays
        $this->models = (array) $this->models;
        $this->operations = (array) $this->operations;

        // We want to add operations differently than adding the other properties
        $defaultKeys[] = 'operations';

        // Create operations for each operation
        if (isset($config['operations'])) {
            foreach ($config['operations'] as $name => $operation) {
                if (!($operation instanceof Operation) && !is_array($operation)) {
                    throw new InvalidArgumentException('Invalid operation in service description: '
                        . gettype($operation));
                }
                $this->operations[$name] = $operation;
            }
        }

        // Get all of the additional properties of the service description and store them in a data array
        foreach (array_diff(array_keys($config), $defaultKeys) as $key) {
            $this->extraData[$key] = $config[$key];
        }
    }
}
<?php

namespace Guzzle\Service\Description;

/**
 * A ServiceDescription stores service information based on a service document
 */
interface ServiceDescriptionInterface extends \Serializable
{
    /**
     * Get the basePath/baseUrl of the description
     *
     * @return string
     */
    public function getBaseUrl();

    /**
     * Get the API operations of the service
     *
     * @return array Returns an array of {@see OperationInterface} objects
     */
    public function getOperations();

    /**
     * Check if the service has an operation by name
     *
     * @param string $name Name of the operation to check
     *
     * @return bool
     */
    public function hasOperation($name);

    /**
     * Get an API operation by name
     *
     * @param string $name Name of the command
     *
     * @return OperationInterface|null
     */
    public function getOperation($name);

    /**
     * Get a specific model from the description
     *
     * @param string $id ID of the model
     *
     * @return Parameter|null
     */
    public function getModel($id);

    /**
     * Get all service description models
     *
     * @return array
     */
    public function getModels();

    /**
     * Check if the description has a specific model by name
     *
     * @param string $id ID of the model
     *
     * @return bool
     */
    public function hasModel($id);

    /**
     * Get the API version of the service
     *
     * @return string
     */
    public function getApiVersion();

    /**
     * Get the name of the API
     *
     * @return string
     */
    public function getName();

    /**
     * Get a summary of the purpose of the API
     *
     * @return string
     */
    public function getDescription();

    /**
     * Get arbitrary data from the service description that is not part of the Guzzle spec
     *
     * @param string $key Data key to retrieve
     *
     * @return null|mixed
     */
    public function getData($key);

    /**
     * Set arbitrary data on the service description
     *
     * @param string $key   Data key to set
     * @param mixed  $value Value to set
     *
     * @return self
     */
    public function setData($key, $value);
}
<?php

namespace Guzzle\Service\Description;

use Guzzle\Service\AbstractConfigLoader;
use Guzzle\Service\Exception\DescriptionBuilderException;

/**
 * Loader for service descriptions
 */
class ServiceDescriptionLoader extends AbstractConfigLoader
{
    protected function build($config, array $options)
    {
        $operations = array();
        if (!empty($config['operations'])) {
            foreach ($config['operations'] as $name => $op) {
                $name = $op['name'] = isset($op['name']) ? $op['name'] : $name;
                // Extend other operations
                if (!empty($op['extends'])) {
                    $this->resolveExtension($name, $op, $operations);
                }
                $op['parameters'] = isset($op['parameters']) ? $op['parameters'] : array();
                $operations[$name] = $op;
            }
        }

        return new ServiceDescription(array(
            'apiVersion'  => isset($config['apiVersion']) ? $config['apiVersion'] : null,
            'baseUrl'     => isset($config['baseUrl']) ? $config['baseUrl'] : null,
            'description' => isset($config['description']) ? $config['description'] : null,
            'operations'  => $operations,
            'models'      => isset($config['models']) ? $config['models'] : null
        ) + $config);
    }

    /**
     * @param string $name       Name of the operation
     * @param array  $op         Operation value array
     * @param array  $operations Currently loaded operations
     * @throws DescriptionBuilderException when extending a non-existent operation
     */
    protected function resolveExtension($name, array &$op, array &$operations)
    {
        $resolved = array();
        $original = empty($op['parameters']) ? false: $op['parameters'];
        $hasClass = !empty($op['class']);
        foreach ((array) $op['extends'] as $extendedCommand) {
            if (empty($operations[$extendedCommand])) {
                throw new DescriptionBuilderException("{$name} extends missing operation {$extendedCommand}");
            }
            $toArray = $operations[$extendedCommand];
            $resolved = empty($resolved)
                ? $toArray['parameters']
                : array_merge($resolved, $toArray['parameters']);

            $op = $op + $toArray;
            if (!$hasClass && isset($toArray['class'])) {
                $op['class'] = $toArray['class'];
            }
        }
        $op['parameters'] = $original ? array_merge($resolved, $original) : $resolved;
    }
}
<?php

namespace Guzzle\Service\Description;

/**
 * Validator responsible for preparing and validating parameters against the parameter's schema
 */
interface ValidatorInterface
{
    /**
     * Validate a value against the acceptable types, regular expressions, minimum, maximums, instanceOf, enums, etc
     * Add default and static values to the passed in variable. If the validation completes successfully, the input
     * must be run correctly through the matching schema's filters attribute.
     *
     * @param Parameter $param Schema that is being validated against the value
     * @param mixed     $value Value to validate and process. The value may change during this process.
     *
     * @return bool  Returns true if the input data is valid for the schema
     */
    public function validate(Parameter $param, &$value);

    /**
     * Get validation errors encountered while validating
     *
     * @return array
     */
    public function getErrors();
}
<?php

namespace Guzzle\Service\Exception;

use Guzzle\Common\Exception\RuntimeException;

class CommandException extends RuntimeException {}
<?php

namespace Guzzle\Service\Exception;

use Guzzle\Http\Exception\MultiTransferException;
use Guzzle\Service\Command\CommandInterface;

/**
 * Exception thrown when transferring commands in parallel
 */
class CommandTransferException extends MultiTransferException
{
    protected $successfulCommands = array();
    protected $failedCommands = array();

    /**
     * Creates a new CommandTransferException from a MultiTransferException
     *
     * @param MultiTransferException $e Exception to base a new exception on
     *
     * @return self
     */
    public static function fromMultiTransferException(MultiTransferException $e)
    {
        $ce = new self($e->getMessage(), $e->getCode(), $e->getPrevious());
        $ce->setSuccessfulRequests($e->getSuccessfulRequests());

        $alreadyAddedExceptions = array();
        foreach ($e->getFailedRequests() as $request) {
            if ($re = $e->getExceptionForFailedRequest($request)) {
                $alreadyAddedExceptions[] = $re;
                $ce->addFailedRequestWithException($request, $re);
            } else {
                $ce->addFailedRequest($request);
            }
        }

        // Add any exceptions that did not map to a request
        if (count($alreadyAddedExceptions) < count($e)) {
            foreach ($e as $ex) {
                if (!in_array($ex, $alreadyAddedExceptions)) {
                    $ce->add($ex);
                }
            }
        }

        return $ce;
    }

    /**
     * Get all of the commands in the transfer
     *
     * @return array
     */
    public function getAllCommands()
    {
        return array_merge($this->successfulCommands, $this->failedCommands);
    }

    /**
     * Add to the array of successful commands
     *
     * @param CommandInterface $command Successful command
     *
     * @return self
     */
    public function addSuccessfulCommand(CommandInterface $command)
    {
        $this->successfulCommands[] = $command;

        return $this;
    }

    /**
     * Add to the array of failed commands
     *
     * @param CommandInterface $command Failed command
     *
     * @return self
     */
    public function addFailedCommand(CommandInterface $command)
    {
        $this->failedCommands[] = $command;

        return $this;
    }

    /**
     * Get an array of successful commands
     *
     * @return array
     */
    public function getSuccessfulCommands()
    {
        return $this->successfulCommands;
    }

    /**
     * Get an array of failed commands
     *
     * @return array
     */
    public function getFailedCommands()
    {
        return $this->failedCommands;
    }

    /**
     * Get the Exception that caused the given $command to fail
     *
     * @param CommandInterface $command Failed command
     *
     * @return \Exception|null
     */
    public function getExceptionForFailedCommand(CommandInterface $command)
    {
        return $this->getExceptionForFailedRequest($command->getRequest());
    }
}
<?php

namespace Guzzle\Service\Exception;

use Guzzle\Common\Exception\RuntimeException;

class DescriptionBuilderException extends RuntimeException {}
<?php

namespace Guzzle\Service\Exception;

use Guzzle\Common\Exception\RuntimeException;

/**
 * Command transfer exception when commands do not all use the same client
 */
class InconsistentClientTransferException extends RuntimeException
{
    /**
     * @var array Commands with an invalid client
     */
    private $invalidCommands = array();

    /**
     * @param array $commands Invalid commands
     */
    public function __construct(array $commands)
    {
        $this->invalidCommands = $commands;
        parent::__construct(
            'Encountered commands in a batch transfer that use inconsistent clients. The batching ' .
            'strategy you use with a command transfer must divide command batches by client.'
        );
    }

    /**
     * Get the invalid commands
     *
     * @return array
     */
    public function getCommands()
    {
        return $this->invalidCommands;
    }
}
<?php

namespace Guzzle\Service\Exception;

use Guzzle\Common\Exception\RuntimeException;

class ResponseClassException extends RuntimeException
{
}
<?php

namespace Guzzle\Service\Exception;

use Guzzle\Common\Exception\RuntimeException;

class ServiceBuilderException extends RuntimeException {}
<?php

namespace Guzzle\Service\Exception;

class ServiceNotFoundException extends ServiceBuilderException {}
<?php

namespace Guzzle\Service\Exception;

use Guzzle\Common\Exception\RuntimeException;

class ValidationException extends RuntimeException
{
    protected $errors = array();

    /**
     * Set the validation error messages
     *
     * @param array $errors Array of validation errors
     */
    public function setErrors(array $errors)
    {
        $this->errors = $errors;
    }

    /**
     * Get any validation errors
     *
     * @return array
     */
    public function getErrors()
    {
        return $this->errors;
    }
}
<?php

namespace Guzzle\Service\Resource;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Service\Command\CommandInterface;

/**
 * Abstract resource iterator factory implementation
 */
abstract class AbstractResourceIteratorFactory implements ResourceIteratorFactoryInterface
{
    public function build(CommandInterface $command, array $options = array())
    {
        if (!$this->canBuild($command)) {
            throw new InvalidArgumentException('Iterator was not found for ' . $command->getName());
        }

        $className = $this->getClassName($command);

        return new $className($command, $options);
    }

    public function canBuild(CommandInterface $command)
    {
        return (bool) $this->getClassName($command);
    }

    /**
     * Get the name of the class to instantiate for the command
     *
     * @param CommandInterface $command Command that is associated with the iterator
     *
     * @return string
     */
    abstract protected function getClassName(CommandInterface $command);
}
<?php

namespace Guzzle\Service\Resource;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Service\Command\CommandInterface;

/**
 * Factory that utilizes multiple factories for creating iterators
 */
class CompositeResourceIteratorFactory implements ResourceIteratorFactoryInterface
{
    /** @var array Array of factories */
    protected $factories;

    /** @param array $factories Array of factories used to instantiate iterators */
    public function __construct(array $factories)
    {
        $this->factories = $factories;
    }

    public function build(CommandInterface $command, array $options = array())
    {
        if (!($factory = $this->getFactory($command))) {
            throw new InvalidArgumentException('Iterator was not found for ' . $command->getName());
        }

        return $factory->build($command, $options);
    }

    public function canBuild(CommandInterface $command)
    {
        return $this->getFactory($command) !== false;
    }

    /**
     * Add a factory to the composite factory
     *
     * @param ResourceIteratorFactoryInterface $factory Factory to add
     *
     * @return self
     */
    public function addFactory(ResourceIteratorFactoryInterface $factory)
    {
        $this->factories[] = $factory;

        return $this;
    }

    /**
     * Get the factory that matches the command object
     *
     * @param CommandInterface $command Command retrieving the iterator for
     *
     * @return ResourceIteratorFactoryInterface|bool
     */
    protected function getFactory(CommandInterface $command)
    {
        foreach ($this->factories as $factory) {
            if ($factory->canBuild($command)) {
                return $factory;
            }
        }

        return false;
    }
}
<?php

namespace Guzzle\Service\Resource;

use Guzzle\Service\Command\CommandInterface;

/**
 * Resource iterator factory used when explicitly mapping strings to iterator classes
 */
class MapResourceIteratorFactory extends AbstractResourceIteratorFactory
{
    /** @var array Associative array mapping iterator names to class names */
    protected $map;

    /** @param array $map Associative array mapping iterator names to class names */
    public function __construct(array $map)
    {
        $this->map = $map;
    }

    public function getClassName(CommandInterface $command)
    {
        $className = $command->getName();

        if (isset($this->map[$className])) {
            return $this->map[$className];
        } elseif (isset($this->map['*'])) {
            // If a wildcard was added, then always use that
            return $this->map['*'];
        }

        return null;
    }
}
<?php

namespace Guzzle\Service\Resource;

use Guzzle\Common\Collection;
use Guzzle\Service\Description\Parameter;

/**
 * Default model created when commands create service description model responses
 */
class Model extends Collection
{
    /** @var Parameter Structure of the model */
    protected $structure;

    /**
     * @param array     $data      Data contained by the model
     * @param Parameter $structure The structure of the model
     */
    public function __construct(array $data = array(), Parameter $structure = null)
    {
        $this->data = $data;
        $this->structure = $structure;
    }

    /**
     * Get the structure of the model
     *
     * @return Parameter
     */
    public function getStructure()
    {
        return $this->structure ?: new Parameter();
    }

    /**
     * Provides debug information about the model object
     *
     * @return string
     */
    public function __toString()
    {
        $output = 'Debug output of ';
        if ($this->structure) {
            $output .= $this->structure->getName() . ' ';
        }
        $output .= 'model';
        $output = str_repeat('=', strlen($output)) . "\n" . $output . "\n" . str_repeat('=', strlen($output)) . "\n\n";
        $output .= "Model data\n-----------\n\n";
        $output .= "This data can be retrieved from the model object using the get() method of the model "
            . "(e.g. \$model->get(\$key)) or accessing the model like an associative array (e.g. \$model['key']).\n\n";
        $lines = array_slice(explode("\n", trim(print_r($this->toArray(), true))), 2, -1);
        $output .=  implode("\n", $lines);

        if ($this->structure) {
            $output .= "\n\nModel structure\n---------------\n\n";
            $output .= "The following JSON document defines how the model was parsed from an HTTP response into the "
                . "associative array structure you see above.\n\n";
            $output .= '  ' . json_encode($this->structure->toArray()) . "\n\n";
        }

        return $output . "\n";
    }
}
<?php

namespace Guzzle\Service\Resource;

use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Service\Command\CommandInterface;

abstract class ResourceIterator extends AbstractHasDispatcher implements ResourceIteratorInterface
{
    /** @var CommandInterface Command used to send requests */
    protected $command;

    /** @var CommandInterface First sent command */
    protected $originalCommand;

    /** @var array Currently loaded resources */
    protected $resources;

    /** @var int Total number of resources that have been retrieved */
    protected $retrievedCount = 0;

    /** @var int Total number of resources that have been iterated */
    protected $iteratedCount = 0;

    /** @var string NextToken/Marker for a subsequent request */
    protected $nextToken = false;

    /** @var int Maximum number of resources to fetch per request */
    protected $pageSize;

    /** @var int Maximum number of resources to retrieve in total */
    protected $limit;

    /** @var int Number of requests sent */
    protected $requestCount = 0;

    /** @var array Initial data passed to the constructor */
    protected $data = array();

    /** @var bool Whether or not the current value is known to be invalid */
    protected $invalid;

    public static function getAllEvents()
    {
        return array(
            // About to issue another command to get more results
            'resource_iterator.before_send',
            // Issued another command to get more results
            'resource_iterator.after_send'
        );
    }

    /**
     * @param CommandInterface $command Initial command used for iteration
     * @param array            $data    Associative array of additional parameters. You may specify any number of custom
     *     options for an iterator. Among these options, you may also specify the following values:
     *     - limit: Attempt to limit the maximum number of resources to this amount
     *     - page_size: Attempt to retrieve this number of resources per request
     */
    public function __construct(CommandInterface $command, array $data = array())
    {
        // Clone the command to keep track of the originating command for rewind
        $this->originalCommand = $command;

        // Parse options from the array of options
        $this->data = $data;
        $this->limit = array_key_exists('limit', $data) ? $data['limit'] : 0;
        $this->pageSize = array_key_exists('page_size', $data) ? $data['page_size'] : false;
    }

    /**
     * Get all of the resources as an array (Warning: this could issue a large number of requests)
     *
     * @return array
     */
    public function toArray()
    {
        return iterator_to_array($this, false);
    }

    public function setLimit($limit)
    {
        $this->limit = $limit;
        $this->resetState();

        return $this;
    }

    public function setPageSize($pageSize)
    {
        $this->pageSize = $pageSize;
        $this->resetState();

        return $this;
    }

    /**
     * Get an option from the iterator
     *
     * @param string $key Key of the option to retrieve
     *
     * @return mixed|null Returns NULL if not set or the value if set
     */
    public function get($key)
    {
        return array_key_exists($key, $this->data) ? $this->data[$key] : null;
    }

    /**
     * Set an option on the iterator
     *
     * @param string $key   Key of the option to set
     * @param mixed  $value Value to set for the option
     *
     * @return ResourceIterator
     */
    public function set($key, $value)
    {
        $this->data[$key] = $value;

        return $this;
    }

    public function current()
    {
        return $this->resources ? current($this->resources) : false;
    }

    public function key()
    {
        return max(0, $this->iteratedCount - 1);
    }

    public function count()
    {
        return $this->retrievedCount;
    }

    /**
     * Get the total number of requests sent
     *
     * @return int
     */
    public function getRequestCount()
    {
        return $this->requestCount;
    }

    /**
     * Rewind the Iterator to the first element and send the original command
     */
    public function rewind()
    {
        // Use the original command
        $this->command = clone $this->originalCommand;
        $this->resetState();
        $this->next();
    }

    public function valid()
    {
        return !$this->invalid && (!$this->resources || $this->current() || $this->nextToken)
            && (!$this->limit || $this->iteratedCount < $this->limit + 1);
    }

    public function next()
    {
        $this->iteratedCount++;

        // Check if a new set of resources needs to be retrieved
        $sendRequest = false;
        if (!$this->resources) {
            $sendRequest = true;
        } else {
            // iterate over the internal array
            $current = next($this->resources);
            $sendRequest = $current === false && $this->nextToken && (!$this->limit || $this->iteratedCount < $this->limit + 1);
        }

        if ($sendRequest) {

            $this->dispatch('resource_iterator.before_send', array(
                'iterator'  => $this,
                'resources' => $this->resources
            ));

            // Get a new command object from the original command
            $this->command = clone $this->originalCommand;
            // Send a request and retrieve the newly loaded resources
            $this->resources = $this->sendRequest();
            $this->requestCount++;

            // If no resources were found, then the last request was not needed
            // and iteration must stop
            if (empty($this->resources)) {
                $this->invalid = true;
            } else {
                // Add to the number of retrieved resources
                $this->retrievedCount += count($this->resources);
                // Ensure that we rewind to the beginning of the array
                reset($this->resources);
            }

            $this->dispatch('resource_iterator.after_send', array(
                'iterator'  => $this,
                'resources' => $this->resources
            ));
        }
    }

    /**
     * Retrieve the NextToken that can be used in other iterators.
     *
     * @return string Returns a NextToken
     */
    public function getNextToken()
    {
        return $this->nextToken;
    }

    /**
     * Returns the value that should be specified for the page size for a request that will maintain any hard limits,
     * but still honor the specified pageSize if the number of items retrieved + pageSize < hard limit
     *
     * @return int Returns the page size of the next request.
     */
    protected function calculatePageSize()
    {
        if ($this->limit && $this->iteratedCount + $this->pageSize > $this->limit) {
            return 1 + ($this->limit - $this->iteratedCount);
        }

        return (int) $this->pageSize;
    }

    /**
     * Reset the internal state of the iterator without triggering a rewind()
     */
    protected function resetState()
    {
        $this->iteratedCount = 0;
        $this->retrievedCount = 0;
        $this->nextToken = false;
        $this->resources = null;
        $this->invalid = false;
    }

    /**
     * Send a request to retrieve the next page of results. Hook for subclasses to implement.
     *
     * @return array Returns the newly loaded resources
     */
    abstract protected function sendRequest();
}
<?php

namespace Guzzle\Service\Resource;

use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Batch\BatchBuilder;
use Guzzle\Batch\BatchSizeDivisor;
use Guzzle\Batch\BatchClosureTransfer;
use Guzzle\Common\Version;

/**
 * Apply a callback to the contents of a {@see ResourceIteratorInterface}
 * @deprecated Will be removed in a future version and is no longer maintained. Use the Batch\ abstractions instead.
 * @codeCoverageIgnore
 */
class ResourceIteratorApplyBatched extends AbstractHasDispatcher
{
    /** @var callable|array */
    protected $callback;

    /** @var ResourceIteratorInterface */
    protected $iterator;

    /** @var integer Total number of sent batches */
    protected $batches = 0;

    /** @var int Total number of iterated resources */
    protected $iterated = 0;

    public static function getAllEvents()
    {
        return array(
            // About to send a batch of requests to the callback
            'iterator_batch.before_batch',
            // Finished sending a batch of requests to the callback
            'iterator_batch.after_batch',
            // Created the batch object
            'iterator_batch.created_batch'
        );
    }

    /**
     * @param ResourceIteratorInterface $iterator Resource iterator to apply a callback to
     * @param array|callable            $callback Callback method accepting the resource iterator
     *                                            and an array of the iterator's current resources
     */
    public function __construct(ResourceIteratorInterface $iterator, $callback)
    {
        $this->iterator = $iterator;
        $this->callback = $callback;
        Version::warn(__CLASS__ . ' is deprecated');
    }

    /**
     * Apply the callback to the contents of the resource iterator
     *
     * @param int $perBatch The number of records to group per batch transfer
     *
     * @return int Returns the number of iterated resources
     */
    public function apply($perBatch = 50)
    {
        $this->iterated = $this->batches = $batches = 0;
        $that = $this;
        $it = $this->iterator;
        $callback = $this->callback;

        $batch = BatchBuilder::factory()
            ->createBatchesWith(new BatchSizeDivisor($perBatch))
            ->transferWith(new BatchClosureTransfer(function (array $batch) use ($that, $callback, &$batches, $it) {
                $batches++;
                $that->dispatch('iterator_batch.before_batch', array('iterator' => $it, 'batch' => $batch));
                call_user_func_array($callback, array($it, $batch));
                $that->dispatch('iterator_batch.after_batch', array('iterator' => $it, 'batch' => $batch));
            }))
            ->autoFlushAt($perBatch)
            ->build();

        $this->dispatch('iterator_batch.created_batch', array('batch' => $batch));

        foreach ($this->iterator as $resource) {
            $this->iterated++;
            $batch->add($resource);
        }

        $batch->flush();
        $this->batches = $batches;

        return $this->iterated;
    }

    /**
     * Get the total number of batches sent
     *
     * @return int
     */
    public function getBatchCount()
    {
        return $this->batches;
    }

    /**
     * Get the total number of iterated resources
     *
     * @return int
     */
    public function getIteratedCount()
    {
        return $this->iterated;
    }
}
<?php

namespace Guzzle\Service\Resource;

use Guzzle\Inflection\InflectorInterface;
use Guzzle\Inflection\Inflector;
use Guzzle\Service\Command\CommandInterface;

/**
 * Factory for creating {@see ResourceIteratorInterface} objects using a convention of storing iterator classes under a
 * root namespace using the name of a {@see CommandInterface} object as a convention for determining the name of an
 * iterator class. The command name is converted to CamelCase and Iterator is appended (e.g. abc_foo => AbcFoo).
 */
class ResourceIteratorClassFactory extends AbstractResourceIteratorFactory
{
    /** @var array List of namespaces used to look for classes */
    protected $namespaces;

    /** @var InflectorInterface Inflector used to determine class names */
    protected $inflector;

    /**
     * @param string|array       $namespaces List of namespaces for iterator objects
     * @param InflectorInterface $inflector  Inflector used to resolve class names
     */
    public function __construct($namespaces = array(), InflectorInterface $inflector = null)
    {
        $this->namespaces = (array) $namespaces;
        $this->inflector = $inflector ?: Inflector::getDefault();
    }

    /**
     * Registers a namespace to check for Iterators
     *
     * @param string $namespace Namespace which contains Iterator classes
     *
     * @return self
     */
    public function registerNamespace($namespace)
    {
        array_unshift($this->namespaces, $namespace);

        return $this;
    }

    protected function getClassName(CommandInterface $command)
    {
        $iteratorName = $this->inflector->camel($command->getName()) . 'Iterator';

        // Determine the name of the class to load
        foreach ($this->namespaces as $namespace) {
            $potentialClassName = $namespace . '\\' . $iteratorName;
            if (class_exists($potentialClassName)) {
                return $potentialClassName;
            }
        }

        return false;
    }
}
<?php

namespace Guzzle\Service\Resource;

use Guzzle\Service\Command\CommandInterface;

/**
 * Factory for creating {@see ResourceIteratorInterface} objects
 */
interface ResourceIteratorFactoryInterface
{
    /**
     * Create a resource iterator
     *
     * @param CommandInterface $command Command to create an iterator for
     * @param array                 $options Iterator options that are exposed as data.
     *
     * @return ResourceIteratorInterface
     */
    public function build(CommandInterface $command, array $options = array());

    /**
     * Check if the factory can create an iterator
     *
     * @param CommandInterface $command Command to create an iterator for
     *
     * @return bool
     */
    public function canBuild(CommandInterface $command);
}
<?php

namespace Guzzle\Service\Resource;

use Guzzle\Common\HasDispatcherInterface;
use Guzzle\Common\ToArrayInterface;

/**
 * Iterates over a paginated resource using subsequent requests in order to retrieve the entire matching result set
 */
interface ResourceIteratorInterface extends ToArrayInterface, HasDispatcherInterface, \Iterator, \Countable
{
    /**
     * Retrieve the NextToken that can be used in other iterators.
     *
     * @return string Returns a NextToken
     */
    public function getNextToken();

    /**
     * Attempt to limit the total number of resources returned by the iterator.
     *
     * You may still receive more items than you specify. Set to 0 to specify no limit.
     *
     * @param int $limit Limit amount
     *
     * @return ResourceIteratorInterface
     */
    public function setLimit($limit);

    /**
     * Attempt to limit the total number of resources retrieved per request by  the iterator.
     *
     * The iterator may return more than you specify in the page size argument depending on the service and underlying
     * command implementation.  Set to 0 to specify no page size limitation.
     *
     * @param int $pageSize Limit amount
     *
     * @return ResourceIteratorInterface
     */
    public function setPageSize($pageSize);

    /**
     * Get a data option from the iterator
     *
     * @param string $key Key of the option to retrieve
     *
     * @return mixed|null Returns NULL if not set or the value if set
     */
    public function get($key);

    /**
     * Set a data option on the iterator
     *
     * @param string $key   Key of the option to set
     * @param mixed  $value Value to set for the option
     *
     * @return ResourceIteratorInterface
     */
    public function set($key, $value);
}
<?php

namespace Guzzle\Stream;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Url;

/**
 * Factory used to create fopen streams using PHP's http and https stream wrappers
 *
 * Note: PHP's http stream wrapper only supports streaming downloads. It does not support streaming uploads.
 */
class PhpStreamRequestFactory implements StreamRequestFactoryInterface
{
    /** @var resource Stream context options */
    protected $context;

    /** @var array Stream context */
    protected $contextOptions;

    /** @var Url Stream URL */
    protected $url;

    /** @var array Last response headers received by the HTTP request */
    protected $lastResponseHeaders;

    /**
     * {@inheritdoc}
     *
     * The $params array can contain the following custom keys specific to the PhpStreamRequestFactory:
     * - stream_class: The name of a class to create instead of a Guzzle\Stream\Stream object
     */
    public function fromRequest(RequestInterface $request, $context = array(), array $params = array())
    {
        if (is_resource($context)) {
            $this->contextOptions = stream_context_get_options($context);
            $this->context = $context;
        } elseif (is_array($context) || !$context) {
            $this->contextOptions = $context;
            $this->createContext($params);
        } elseif ($context) {
            throw new InvalidArgumentException('$context must be an array or resource');
        }

        // Dispatch the before send event
        $request->dispatch('request.before_send', array(
            'request'         => $request,
            'context'         => $this->context,
            'context_options' => $this->contextOptions
        ));

        $this->setUrl($request);
        $this->addDefaultContextOptions($request);
        $this->addSslOptions($request);
        $this->addBodyOptions($request);
        $this->addProxyOptions($request);

        // Create the file handle but silence errors
        return $this->createStream($params)
            ->setCustomData('request', $request)
            ->setCustomData('response_headers', $this->getLastResponseHeaders());
    }

    /**
     * Set an option on the context and the internal options array
     *
     * @param string $wrapper   Stream wrapper name of http
     * @param string $name      Context name
     * @param mixed  $value     Context value
     * @param bool   $overwrite Set to true to overwrite an existing value
     */
    protected function setContextValue($wrapper, $name, $value, $overwrite = false)
    {
        if (!isset($this->contextOptions[$wrapper])) {
            $this->contextOptions[$wrapper] = array($name => $value);
        } elseif (!$overwrite && isset($this->contextOptions[$wrapper][$name])) {
            return;
        }
        $this->contextOptions[$wrapper][$name] = $value;
        stream_context_set_option($this->context, $wrapper, $name, $value);
    }

    /**
     * Create a stream context
     *
     * @param array $params Parameter array
     */
    protected function createContext(array $params)
    {
        $options = $this->contextOptions;
        $this->context = $this->createResource(function () use ($params, $options) {
            return stream_context_create($options, $params);
        });
    }

    /**
     * Get the last response headers received by the HTTP request
     *
     * @return array
     */
    public function getLastResponseHeaders()
    {
        return $this->lastResponseHeaders;
    }

    /**
     * Adds the default context options to the stream context options
     *
     * @param RequestInterface $request Request
     */
    protected function addDefaultContextOptions(RequestInterface $request)
    {
        $this->setContextValue('http', 'method', $request->getMethod());
        $headers = $request->getHeaderLines();

        // "Connection: close" is required to get streams to work in HTTP 1.1
        if (!$request->hasHeader('Connection')) {
            $headers[] = 'Connection: close';
        }

        $this->setContextValue('http', 'header', $headers);
        $this->setContextValue('http', 'protocol_version', $request->getProtocolVersion());
        $this->setContextValue('http', 'ignore_errors', true);
    }

    /**
     * Set the URL to use with the factory
     *
     * @param RequestInterface $request Request that owns the URL
     */
    protected function setUrl(RequestInterface $request)
    {
        $this->url = $request->getUrl(true);

        // Check for basic Auth username
        if ($request->getUsername()) {
            $this->url->setUsername($request->getUsername());
        }

        // Check for basic Auth password
        if ($request->getPassword()) {
            $this->url->setPassword($request->getPassword());
        }
    }

    /**
     * Add SSL options to the stream context
     *
     * @param RequestInterface $request Request
     */
    protected function addSslOptions(RequestInterface $request)
    {
        if ($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)) {
            $this->setContextValue('ssl', 'verify_peer', true, true);
            if ($cafile = $request->getCurlOptions()->get(CURLOPT_CAINFO)) {
                $this->setContextValue('ssl', 'cafile', $cafile, true);
            }
        } else {
            $this->setContextValue('ssl', 'verify_peer', false, true);
        }
    }

    /**
     * Add body (content) specific options to the context options
     *
     * @param RequestInterface $request
     */
    protected function addBodyOptions(RequestInterface $request)
    {
        // Add the content for the request if needed
        if (!($request instanceof EntityEnclosingRequestInterface)) {
            return;
        }

        if (count($request->getPostFields())) {
            $this->setContextValue('http', 'content', (string) $request->getPostFields(), true);
        } elseif ($request->getBody()) {
            $this->setContextValue('http', 'content', (string) $request->getBody(), true);
        }

        // Always ensure a content-length header is sent
        if (isset($this->contextOptions['http']['content'])) {
            $headers = isset($this->contextOptions['http']['header']) ? $this->contextOptions['http']['header'] : array();
            $headers[] = 'Content-Length: ' . strlen($this->contextOptions['http']['content']);
            $this->setContextValue('http', 'header', $headers, true);
        }
    }

    /**
     * Add proxy parameters to the context if needed
     *
     * @param RequestInterface $request Request
     */
    protected function addProxyOptions(RequestInterface $request)
    {
        if ($proxy = $request->getCurlOptions()->get(CURLOPT_PROXY)) {
            $this->setContextValue('http', 'proxy', $proxy);
        }
    }

    /**
     * Create the stream for the request with the context options
     *
     * @param array $params Parameters of the stream
     *
     * @return StreamInterface
     */
    protected function createStream(array $params)
    {
        $http_response_header = null;
        $url = $this->url;
        $context = $this->context;
        $fp = $this->createResource(function () use ($context, $url, &$http_response_header) {
            return fopen((string) $url, 'r', false, $context);
        });

        // Determine the class to instantiate
        $className = isset($params['stream_class']) ? $params['stream_class'] : __NAMESPACE__ . '\\Stream';

        /** @var $stream StreamInterface */
        $stream = new $className($fp);

        // Track the response headers of the request
        if (isset($http_response_header)) {
            $this->lastResponseHeaders = $http_response_header;
            $this->processResponseHeaders($stream);
        }

        return $stream;
    }

    /**
     * Process response headers
     *
     * @param StreamInterface $stream
     */
    protected function processResponseHeaders(StreamInterface $stream)
    {
        // Set the size on the stream if it was returned in the response
        foreach ($this->lastResponseHeaders as $header) {
            if ((stripos($header, 'Content-Length:')) === 0) {
                $stream->setSize(trim(substr($header, 15)));
            }
        }
    }

    /**
     * Create a resource and check to ensure it was created successfully
     *
     * @param callable $callback Closure to invoke that must return a valid resource
     *
     * @return resource
     * @throws RuntimeException on error
     */
    protected function createResource($callback)
    {
        $errors = null;
        set_error_handler(function ($_, $msg, $file, $line) use (&$errors) {
            $errors[] = array(
                'message' => $msg,
                'file'    => $file,
                'line'    => $line
            );
            return true;
        });
        $resource = call_user_func($callback);
        restore_error_handler();

        if (!$resource) {
            $message = 'Error creating resource. ';
            foreach ($errors as $err) {
                foreach ($err as $key => $value) {
                    $message .= "[$key] $value" . PHP_EOL;
                }
            }
            throw new RuntimeException(trim($message));
        }

        return $resource;
    }
}
<?php

namespace Guzzle\Stream;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * PHP stream implementation
 */
class Stream implements StreamInterface
{
    const STREAM_TYPE = 'stream_type';
    const WRAPPER_TYPE = 'wrapper_type';
    const IS_LOCAL = 'is_local';
    const IS_READABLE = 'is_readable';
    const IS_WRITABLE = 'is_writable';
    const SEEKABLE = 'seekable';

    /** @var resource Stream resource */
    protected $stream;

    /** @var int Size of the stream contents in bytes */
    protected $size;

    /** @var array Stream cached data */
    protected $cache = array();

    /** @var array Custom stream data */
    protected $customData = array();

    /** @var array Hash table of readable and writeable stream types for fast lookups */
    protected static $readWriteHash = array(
        'read' => array(
            'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true,
            'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true,
            'rt' => true, 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a+' => true
        ),
        'write' => array(
            'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, 'c+' => true,
            'wb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true,
            'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true
        )
    );

    /**
     * @param resource $stream Stream resource to wrap
     * @param int      $size   Size of the stream in bytes. Only pass if the size cannot be obtained from the stream.
     *
     * @throws InvalidArgumentException if the stream is not a stream resource
     */
    public function __construct($stream, $size = null)
    {
        $this->setStream($stream, $size);
    }

    /**
     * Closes the stream when the helper is destructed
     */
    public function __destruct()
    {
        $this->close();
    }

    public function __toString()
    {
        if (!$this->isReadable() || (!$this->isSeekable() && $this->isConsumed())) {
            return '';
        }

        $originalPos = $this->ftell();
        $body = stream_get_contents($this->stream, -1, 0);
        $this->seek($originalPos);

        return $body;
    }

    public function close()
    {
        if (is_resource($this->stream)) {
            fclose($this->stream);
        }
        $this->cache[self::IS_READABLE] = false;
        $this->cache[self::IS_WRITABLE] = false;
    }

    /**
     * Calculate a hash of a Stream
     *
     * @param StreamInterface $stream    Stream to calculate the hash for
     * @param string          $algo      Hash algorithm (e.g. md5, crc32, etc)
     * @param bool            $rawOutput Whether or not to use raw output
     *
     * @return bool|string Returns false on failure or a hash string on success
     */
    public static function getHash(StreamInterface $stream, $algo, $rawOutput = false)
    {
        $pos = $stream->ftell();
        if (!$stream->seek(0)) {
            return false;
        }

        $ctx = hash_init($algo);
        while (!$stream->feof()) {
            hash_update($ctx, $stream->read(8192));
        }

        $out = hash_final($ctx, (bool) $rawOutput);
        $stream->seek($pos);

        return $out;
    }

    public function getMetaData($key = null)
    {
        $meta = stream_get_meta_data($this->stream);

        return !$key ? $meta : (array_key_exists($key, $meta) ? $meta[$key] : null);
    }

    public function getStream()
    {
        return $this->stream;
    }

    public function setStream($stream, $size = null)
    {
        if (!is_resource($stream)) {
            throw new InvalidArgumentException('Stream must be a resource');
        }

        $this->size = $size;
        $this->stream = $stream;
        $this->rebuildCache();

        return $this;
    }

    public function detachStream()
    {
        $this->stream = null;

        return $this;
    }

    public function getWrapper()
    {
        return $this->cache[self::WRAPPER_TYPE];
    }

    public function getWrapperData()
    {
        return $this->getMetaData('wrapper_data') ?: array();
    }

    public function getStreamType()
    {
        return $this->cache[self::STREAM_TYPE];
    }

    public function getUri()
    {
        return $this->cache['uri'];
    }

    public function getSize()
    {
        if ($this->size !== null) {
            return $this->size;
        }

        // If the stream is a file based stream and local, then use fstat
        clearstatcache(true, $this->cache['uri']);
        $stats = fstat($this->stream);
        if (isset($stats['size'])) {
            $this->size = $stats['size'];
            return $this->size;
        } elseif ($this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE]) {
            // Only get the size based on the content if the the stream is readable and seekable
            $pos = $this->ftell();
            $this->size = strlen((string) $this);
            $this->seek($pos);
            return $this->size;
        }

        return false;
    }

    public function isReadable()
    {
        return $this->cache[self::IS_READABLE];
    }

    public function isRepeatable()
    {
        return $this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE];
    }

    public function isWritable()
    {
        return $this->cache[self::IS_WRITABLE];
    }

    public function isConsumed()
    {
        return feof($this->stream);
    }

    public function feof()
    {
        return $this->isConsumed();
    }

    public function isLocal()
    {
        return $this->cache[self::IS_LOCAL];
    }

    public function isSeekable()
    {
        return $this->cache[self::SEEKABLE];
    }

    public function setSize($size)
    {
        $this->size = $size;

        return $this;
    }

    public function seek($offset, $whence = SEEK_SET)
    {
        return $this->cache[self::SEEKABLE] ? fseek($this->stream, $offset, $whence) === 0 : false;
    }

    public function read($length)
    {
        return fread($this->stream, $length);
    }

    public function write($string)
    {
        // We can't know the size after writing anything
        $this->size = null;

        return fwrite($this->stream, $string);
    }

    public function ftell()
    {
        return ftell($this->stream);
    }

    public function rewind()
    {
        return $this->seek(0);
    }

    public function readLine($maxLength = null)
    {
        if (!$this->cache[self::IS_READABLE]) {
            return false;
        } else {
            return $maxLength ? fgets($this->getStream(), $maxLength) : fgets($this->getStream());
        }
    }

    public function setCustomData($key, $value)
    {
        $this->customData[$key] = $value;

        return $this;
    }

    public function getCustomData($key)
    {
        return isset($this->customData[$key]) ? $this->customData[$key] : null;
    }

    /**
     * Reprocess stream metadata
     */
    protected function rebuildCache()
    {
        $this->cache = stream_get_meta_data($this->stream);
        $this->cache[self::IS_LOCAL] = stream_is_local($this->stream);
        $this->cache[self::IS_READABLE] = isset(self::$readWriteHash['read'][$this->cache['mode']]);
        $this->cache[self::IS_WRITABLE] = isset(self::$readWriteHash['write'][$this->cache['mode']]);
    }
}
<?php

namespace Guzzle\Stream;

/**
 * OO interface to PHP streams
 */
interface StreamInterface
{
    /**
     * Convert the stream to a string if the stream is readable and the stream is seekable.
     *
     * @return string
     */
    public function __toString();

    /**
     * Close the underlying stream
     */
    public function close();

    /**
     * Get stream metadata
     *
     * @param string $key Specific metadata to retrieve
     *
     * @return array|mixed|null
     */
    public function getMetaData($key = null);

    /**
     * Get the stream resource
     *
     * @return resource
     */
    public function getStream();

    /**
     * Set the stream that is wrapped by the object
     *
     * @param resource $stream Stream resource to wrap
     * @param int      $size   Size of the stream in bytes. Only pass if the size cannot be obtained from the stream.
     *
     * @return self
     */
    public function setStream($stream, $size = null);

    /**
     * Detach the current stream resource
     *
     * @return self
     */
    public function detachStream();

    /**
     * Get the stream wrapper type
     *
     * @return string
     */
    public function getWrapper();

    /**
     * Wrapper specific data attached to this stream.
     *
     * @return array
     */
    public function getWrapperData();

    /**
     * Get a label describing the underlying implementation of the stream
     *
     * @return string
     */
    public function getStreamType();

    /**
     * Get the URI/filename associated with this stream
     *
     * @return string
     */
    public function getUri();

    /**
     * Get the size of the stream if able
     *
     * @return int|bool
     */
    public function getSize();

    /**
     * Check if the stream is readable
     *
     * @return bool
     */
    public function isReadable();

    /**
     * Check if the stream is repeatable
     *
     * @return bool
     */
    public function isRepeatable();

    /**
     * Check if the stream is writable
     *
     * @return bool
     */
    public function isWritable();

    /**
     * Check if the stream has been consumed
     *
     * @return bool
     */
    public function isConsumed();

    /**
     * Alias of isConsumed
     *
     * @return bool
     */
    public function feof();

    /**
     * Check if the stream is a local stream vs a remote stream
     *
     * @return bool
     */
    public function isLocal();

    /**
     * Check if the string is repeatable
     *
     * @return bool
     */
    public function isSeekable();

    /**
     * Specify the size of the stream in bytes
     *
     * @param int $size Size of the stream contents in bytes
     *
     * @return self
     */
    public function setSize($size);

    /**
     * Seek to a position in the stream
     *
     * @param int $offset Stream offset
     * @param int $whence Where the offset is applied
     *
     * @return bool Returns TRUE on success or FALSE on failure
     * @link   http://www.php.net/manual/en/function.fseek.php
     */
    public function seek($offset, $whence = SEEK_SET);

    /**
     * Read data from the stream
     *
     * @param int $length Up to length number of bytes read.
     *
     * @return string|bool Returns the data read from the stream or FALSE on failure or EOF
     */
    public function read($length);

    /**
     * Write data to the stream
     *
     * @param string $string The string that is to be written.
     *
     * @return int|bool Returns the number of bytes written to the stream on success or FALSE on failure.
     */
    public function write($string);

    /**
     * Returns the current position of the file read/write pointer
     *
     * @return int|bool Returns the position of the file pointer or false on error
     */
    public function ftell();

    /**
     * Rewind to the beginning of the stream
     *
     * @return bool Returns true on success or false on failure
     */
    public function rewind();

    /**
     * Read a line from the stream up to the maximum allowed buffer length
     *
     * @param int $maxLength Maximum buffer length
     *
     * @return string|bool
     */
    public function readLine($maxLength = null);

    /**
     * Set custom data on the stream
     *
     * @param string $key   Key to set
     * @param mixed  $value Value to set
     *
     * @return self
     */
    public function setCustomData($key, $value);

    /**
     * Get custom data from the stream
     *
     * @param string $key Key to retrieve
     *
     * @return null|mixed
     */
    public function getCustomData($key);
}
<?php

namespace Guzzle\Stream;

use Guzzle\Http\Message\RequestInterface;

/**
 * Interface used for creating streams from requests
 */
interface StreamRequestFactoryInterface
{
    /**
     * Create a stream based on a request object
     *
     * @param RequestInterface $request Base the stream on a request
     * @param array|resource   $context A stream_context_options resource or array of parameters used to create a
     *                                  stream context.
     * @param array            $params  Optional array of parameters specific to the factory
     *
     * @return StreamInterface Returns a stream object
     * @throws \Guzzle\Common\Exception\RuntimeException if the stream cannot be opened or an error occurs
     */
    public function fromRequest(RequestInterface $request, $context = array(), array $params = array());
}
<?php

namespace JohnStevenson\JsonWorks;

class Document
{
    public $data;
    public $schema;
    public $lastError;
    public $lastPushIndex;
    private $element;
    private $workingData;
    private $validator;

    public function loadData($data, $noException = false)
    {
        $data = $this->getInput($data, false, $noException);
        $this->data = $data ? Utils::dataCopy($data) : null;
        $this->workingData = null;
        return empty($this->lastError);
    }

    public function loadSchema($schema, $noException = false)
    {
        $this->schema = $this->getInput($schema, true, $noException);
        return empty($this->lastError);
    }

    public function addValue($path, $value)
    {
        $this->lastError = null;
        $this->lastPushIndex = 0;
        $pointers = is_array($path) ? $path : Utils::pathDecode($path);
        $value = Utils::dataCopy($value);

        if (empty($pointers)) {
            # empty path, add value to root
            if ($result = (is_object($value) || is_array($value)) && $this->checkData($value, true)) {
                $this->data = $value;
            } else {
                $this->lastError = $this->lastError ?: 'Value must be an object or array';
            }

            return $result;
        }

        if (is_null($this->data)) {
            # data not initiated
            $this->workingData = null;
        } else {
            # data exists so copy it
            $this->workingData = Utils::dataCopy($this->data);
        }

        # create any new keys and get referenced element
        if (!$this->workAdd($pointers, $arrayPush, $addKey)) {
            return false;
        }

        # finally add passed-in value to referenced element
        if ($arrayPush) {
            $this->lastPushIndex = array_push($this->element, $value) - 1;
        } elseif (null !== $addKey) {
            $this->element->$addKey = $value;
        } else {
            $this->element = $value;
        }

        if ($result = $this->checkData($this->workingData, true)) {
            $this->data = $this->workingData;
        }

        return $result;
    }

    public function copyValue($fromPath, $toPath)
    {
        return $this->workMove($fromPath, $toPath, false);
    }

    public function deleteValue($path)
    {
        $pointers = is_array($path) ? $path : Utils::pathDecode($path);

        if ($result = $this->hasValue($pointers, $dummy)) {

            $key = array_pop($pointers);
            $this->hasValue($pointers, $dummy);

            if (0 === strlen($key)) {
                $this->loadData(null);
            } elseif (is_array($this->element)) {
                $key = (int) $key;
                array_splice($this->element, $key, 1);
            } elseif (is_object($this->element)) {
                unset($this->element->$key);
            }
        }

        return $result;
    }

    public function getValue($path, $default = null)
    {
        if (!$this->hasValue($path, $value)) {
            $value = $default;
        }

        return $value;
    }

    public function hasValue($path, &$value)
    {
        $result = false;
        $value = null;

        $pointers = is_array($path) ? $path : Utils::pathDecode($path);

        if ($this->workGet($pointers, false)) {
            $value = Utils::dataCopy($this->element);
            $result = true;
        }

        return $result;
    }

    public function moveValue($fromPath, $toPath)
    {
        return $this->workMove($fromPath, $toPath, true);
    }

    public function tidy($order = false)
    {
        $this->data = Utils::dataPrune($this->data);
        if ($order && $this->schema) {
            $this->data = Utils::dataOrder($this->data, $this->schema->data);
        }
    }

    public function toJson($pretty, $tabs = false)
    {
        $json = Utils::dataToJson($this->data, $pretty);
        if ($tabs && $pretty) {
            $json = preg_replace_callback('/^( +)/m', function ($m) {
                return str_repeat("\t", (int) strlen($m[1]) / 4);
            }, $json);
        }

        return $json;
    }

    public function validate($lax = false)
    {
        return $this->checkData($this->data, $lax);
    }

    protected function getInput($input, $isSchema, $noException)
    {
        $this->lastError = null;
        $input = $this->getInputWork($input, $isSchema);

        if (false === $input) {
            $this->lastError = $this->lastError ?: 'Invalid input';
        }

        if ($this->lastError !== null && !$noException) {
            throw new \RuntimeException($this->lastError);
        }

        return $this->lastError ? null : $input;
    }

    protected function getInputWork($input, $isSchema)
    {
        if (is_string($input)) {

            if (!preg_match('/^(\{|\[)/', $input)) {
                $filename = $input;

                if (!$input = @file_get_contents($filename)) {
                    if (false === $input) {
                        $this->lastError = 'Unable to open file: '.$filename;
                    } else {
                        $this->lastError = 'File is empty: '.$filename;
                    }

                    return false;
                }
            }

            $input = json_decode($input);
            if (json_last_error()) {
                return false;
            }
        }

        if (is_array($input) || is_null($input)) {
            $result = !$isSchema;
        } else {
            $result = is_object($input);
        }

        if ($result && $isSchema) {
            try {
                $input = new Schema\Model($input);
            } catch (\RuntimeException $e) {
                $result = false;
                $this->lastError = 'Schema error: '.$e->getMessage();
            }
        }

        return $result ? $input : false;
    }

    protected function pushKey($value)
    {
        return (bool) preg_match('/^((-)|(0+))$/', $value);
    }

    protected function arrayKey($value, &$index, $any = false)
    {
        $index = null;

        if ($any && '-' === $value) {
            $index = '-';
        } elseif (preg_match('/^0*\d+$/', $value)) {
            $index = (int) $value;
        }

        return null !== $index;
    }

    protected function workAdd($pointers, &$arrayPush, &$addKey)
    {
        $this->workGet($pointers, true);
        $arrayPush = false;
        $addKey  = null;

        if (is_null($this->element)) {
            if (!$this->workAddElement($pointers)) {
                return false;
            }
        }

        while (!empty($pointers)) {

            $key = array_shift($pointers);

            if (!empty($pointers)) {

                if (is_array($this->element)) {

                    if (!$this->pushKey($key)) {
                        $this->lastError = 'Invalid array key';
                        return false;
                    }

                    $this->element[0] = null;
                    $this->element = &$this->element[0];
                    if (!$this->workAddElement($pointers)) {
                        return false;
                    }

                } else {

                    $this->element->$key = null;
                    $this->element = &$this->element->$key;

                    if (!$this->workAddElement($pointers)) {
                        return false;
                    }
                }

            } else {
                 # no more pointers. First check for array with final array key

                if (is_array($this->element)) {

                    if ($this->arrayKey($key, $index, true)) {
                        $index = is_int($index) ? $index : count($this->element);
                        $arrayPush = $index === count($this->element);
                    }

                    if (!$arrayPush) {
                        $this->lastError = 'Bad array index';
                        return false;
                    }

                } else {
                    $addKey = $key;
                }
            }
        }

        return true;
    }

    protected function workAddElement($pointers)
    {
        $arrayFirst = $this->pushKey($pointers[0]);
        $this->element = $arrayFirst ? array() : new \stdClass();

        if (!$result = $this->checkData($this->workingData, true)) {
            $this->element = !$arrayFirst ? array() : new \stdClass();
            $result = $this->checkData($this->workingData, true);
        }

        return $result;
    }

    protected function workGet(&$pointers, $forEdit)
    {
        if ($forEdit) {
            $this->element = &$this->workingData;
        } else {
            $this->element = &$this->data;
        }

        if (is_null($this->element)) {
            return false;
        }

        while ($pointers) {
            $type = gettype($this->element);
            $test = $pointers;
            $key = array_shift($test);
            $result = false;

            if ('object' === $type) {

                if ($result = property_exists($this->element, $key)) {
                    $this->element = &$this->element->$key;
                }

            } elseif ('array' === $type) {

                if ($result = $this->arrayKey($key, $index)) {
                    if ($result = array_key_exists($index, $this->element)) {
                        $this->element = &$this->element[$index];
                    }
                }
            }

            if (!$result) {
                return false;
            }

            array_shift($pointers);
        }

        return true;
    }

    protected function workMove($fromPath, $toPath, $delete)
    {
        $result = false;

        if ($this->hasValue($fromPath, $value)) {
            if ($result = $this->addValue($toPath, $value)) {
                if ($delete) {
                    $this->deleteValue($fromPath);
                }
            }
        }

        return $result;
    }

    protected function checkData($data, $lax = false)
    {
        if (!$this->schema) {
            return true;
        }

        if (!$this->validator) {
            $this->validator = new Schema\Validator();
        }

        if (!$result = $this->validator->check($data, $this->schema, $lax)) {
            $this->lastError = $this->validator->error;
        }

        return $result;
    }
}
<?php

namespace JohnStevenson\JsonWorks\Schema;

use \JohnStevenson\JsonWorks\Utils;

class Constraints
{
    protected $path;
    protected $lax;

    public function __construct($lax)
    {
        $this->lax = $lax;
    }

    public function validate($data, $schema, $key = null)
    {
        $this->path = Utils::pathAdd($this->path, $key);

        $this->validateCommon($data, $schema);
        $type = gettype($data);

        switch ($type) {
            case 'object':
                $this->validateObject($data, $schema);
                break;
            case 'array':
                $this->validateArray($data, $schema);
                break;
            case 'double':
                # no break
            case 'integer':
                $this->validateNumber($data, $schema);
                break;
            case 'string':
                $this->validateString($data, $schema);
                break;
        }
    }

    protected function validateCommon($data, $schema)
    {
        $common = array('enum', 'type', 'allOf', 'anyOf', 'oneOf', 'not');

        foreach ($common as $name) {

            if ($value = Utils::get($schema, $name)) {

                switch ($name) {
                    case 'enum':
                        $this->validateEnum($value, $data);
                        break;
                    case 'type':
                        $this->validateType($value, $data);
                        break;
                    case 'not':
                        $this->validateNot($value, $data);
                        break;
                    default:
                        $this->validateOf($name, $value, $data);
                }
            }
        }
    }

    protected function validateObject($data, $schema)
    {

        # maxProperties
        if (isset($schema->maxProperties)) {
            $this->validateMaxMin($data, $schema->maxProperties, true);
        }

        if (!$this->lax) {

            # minProperties
            if (isset($schema->minProperties)) {
                $this->validateMaxMin($data, $schema->minProperties, false);
            }

            if (isset($schema->required)) {
                foreach ((array) $schema->required as $name) {
                    if (!isset($data->$name)) {
                        $this->throwError(sprintf("is missing required property '%s'", $name));
                    }
                }
            }

        }

        # additionalProperties
        $additional = Utils::get($schema, 'additionalProperties', true);

        if (false === $additional) {
            $this->validateObjectWork($data, $schema);
        }

        $this->validateObjectChildren($data, $schema, $additional);
    }

    protected function validateArray($data, $schema)
    {

        # maxItems
        if (isset($schema->maxItems)) {
            $this->validateMaxMin($data, $schema->maxItems, true);
        }

        if (!$this->lax) {

            # minItems
            if (isset($schema->minItems)) {
                $this->validateMaxMin($data, $schema->minItems, false);
            }
        }

        # uniqueItems
        if (Utils::get($schema, 'uniqueItems', false)) {
            if (!Utils::uniqueArray($data, true)) {
                $this->throwError('contains duplicate values');
            }
        }

        # items
        $items = Utils::get($schema, 'items', array());

        # additionalItems
        $additional = Utils::get($schema, 'additionalItems', true);

        if (false === $additional && is_array($items)) {
            if (count($data) > count($items)) {
                $this->throwError('contains more elements than are allowed');
            }
        }

        $this->validateArrayChildren($data, $items, $additional);
    }

    protected function validateNumber($data, $schema)
    {
        # maximum
        if (isset($schema->maximum)) {
            $max = $schema->maximum;

            if ($exclusive = Utils::get($schema, 'exclusiveMaximum', false)) {
                $valid = $data < $max;
            } else {
                $valid = $data <= $max;
            }

            if (!$valid) {
                $error = 'value must be less than ';
                $error .= $exclusive ? 'or equal to ' : '';
                $this->throwError($error.$max);
            }
        }

        # minimum
        if (isset($schema->minimum)) {
            $min = $schema->minimum;

            if ($exclusive = Utils::get($schema, 'exclusiveMinimum', false)) {
                $valid = $data > $min;
            } else {
                $valid = $data >= $min;
            }

            if (!$valid) {
                $error = 'value must be greater than ';
                $error .= $exclusive ? '' : 'or equal to ';
                $this->throwError($error.$min);
            }
        }
    }

    protected function validateString($data, $schema)
    {
        # maxLength
        if (isset($schema->maxLength)) {
            if (strlen($data) > $schema->maxLength) {
                $this->throwError(sprintf('has too many characters, maximum (%d)', $schema->maxLength));
            }
        }

        # minLength
        if (isset($schema->minLength)) {
            if (strlen($data) < $schema->minLength) {
                $this->throwError(sprintf('has too few characters, minimum (%d)', $schema->minLength));
            }
        }

        # pattern
        if (isset($schema->pattern)) {
            if (!$this->match($schema->pattern, $data)) {
                $this->throwError(sprintf('does not match pattern: %s', $schema->pattern));
            }
        }

        # format
        if (isset($schema->format)) {
            $this->validateFormat($data, $schema->format);
        }
    }

    protected function validateEnum($enum, $data)
    {
        $result = false;

        foreach ((array) $enum as $value) {
            if ($result = Utils::equals($value, $data)) {
                break;
            }
        }

        if (!$result) {
            $this->throwError('value not found in enumeration '.json_encode($enum));
        }
    }

    protected function validateType($type, $data)
    {
        $types = (array) $type;
        $result = false;

        foreach ($types as $type) {
            if (Utils::checkType($type, $data)) {
                $result = true;
                break;
            }
        }

        if (!$result) {
            $this->throwError(sprintf("value must be of type '%s'", implode(', ', $types)));
        }
    }

    protected function validateNot($not, $data)
    {
        if ($this->validateChild($data, $not)) {
            $this->throwError('must not validate against this schema');
        }
    }

    protected function validateOf($type, $value, $data)
    {
        $matches = 0;
        $result = false;

        foreach ($value as $schema) {

            if ($this->validateChild($data, $schema)) {
                ++$matches;
                if ('anyOf' === $type) {
                    break;
                }
            }
        }

        switch ($type) {
            case 'allOf':
                $result = $matches === count($value);
                break;
            case 'anyOf':
                $result = $matches >= 1;
                break;
            case 'oneOf':
                $result = 1 === $matches;
                break;
        }

        if (!$result) {
            $this->throwError('does not match schema requirements');
        }
    }

    protected function validateMaxMin($data, $value, $isMax)
    {
        $count = count((array) $data);

        if ($isMax && $count > $value) {
            $error = 'has too many members, maximum %d';
        } elseif (!$isMax && $count < $value) {
            $error = 'has too few members, minimum (%d)';
        }

        if (isset($error)) {
            $this->throwError(sprintf($error, $value));
        }
    }

    protected function validateObjectWork($data, $schema)
    {
        $set = (array) $data;
        $p = Utils::get($schema, 'properties', new \stdClass());

        foreach ($p as $key => $value) {
            if (isset($set[$key])) {
                unset($set[$key]);
            }
        }

        $pp = Utils::get($schema, 'patternProperties', new \stdClass());
        $setCopy = $set;

        foreach ($setCopy as $key => $value) {

            foreach ($pp as $regex => $val) {
                if ($this->match($regex, $key)) {
                    unset($set[$key]);
                    break;
                }
            }
        }

        if (!empty($set)) {
            $this->throwError('contains unspecified additional properties');
        }
    }

    protected function validateObjectChildren($data, $schema, $additional)
    {
        if (true === $additional) {
            $additional = new \stdClass();
        }

        $p = Utils::get($schema, 'properties', new \stdClass());
        $pp = Utils::get($schema, 'patternProperties', new \stdClass());

        foreach ($data as $key => $value) {

            $child = array();

            if (isset($p->$key)) {
                $child[] = $p->$key;
            }

            foreach ($pp as $regex => $val) {
                if ($this->match($regex, $key)) {
                    $child[] = $val;
                }
            }

            if (empty($child) && $additional) {
                $child[] = $additional;
            }

            foreach ($child as $subSchema) {
                $this->validateChild($value, $subSchema, $key);
            }
        }
    }

    protected function validateArrayChildren($data, $items, $additional)
    {
        if (null === $items) {
            $items = new \stdClass();
        }

        if (true === $additional) {
            $additional = new \stdClass();
        }

        $single = is_object($items);
        $dataCount = count($data);
        $itemsCount = !$single ? count($items) : 0;

        for ($i = 0; $i < $dataCount; ++$i) {

            if ($single) {
                $subSchema = $items;
            } elseif ($i < $itemsCount) {
                $subSchema = $items[$i];
            } elseif ($additional) {
                $subSchema = $additional;
            } else {
                continue;
            }

            $this->validateChild($data[$i], $subSchema, strval($i));
        }
    }

    protected function validateChild($data, $schema, $key = null)
    {
        $result = true;
        $currentPath = $this->path;

        if (is_null($key)) {
            try {
                $this->validate($data, $schema);
            } catch (ValidationException $e) {
                $result = false;
            }
        } else {
            $this->validate($data, $schema, $key);
        }

        $this->path = $currentPath;

        return $result;
    }

    protected function validateFormat($data, $format)
    {
        switch ($format) {

            case 'date-time':
                $p = '/^\d{4}-\d{2}-\d{2}[T| ]\d{2}:\d{2}:\d{2}(\.\d{1})?(Z|[\+|-]\d{2}:\d{2})?$/i';
                if (!preg_match($p, $data, $match) || false === strtotime($data)) {
                    $this->throwError('Invalid date-time, '.json_encode($data));
                }
                break;

            case 'email':
                if (null === filter_var($data, FILTER_VALIDATE_EMAIL, FILTER_NULL_ON_FAILURE)) {
                    $this->throwError('Invalid email, '.json_encode($data));
                }
                break;

            case 'hostname':
                if (!preg_match('/^[_a-z]+\.([_a-z]+\.?)+$/i', $data)) {
                    $this->throwError('Invalid hostname, '.json_encode($data));
                }
                break;

            case 'ipv4':
                if (null === filter_var($data, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV4)) {
                    $this->throwError('Invalid IPv4 address, '.json_encode($data));
                }
                break;

            case 'ipv6':
                if (null === filter_var($data, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV6)) {
                    $this->throwError('Invalid IPv6 address, '.json_encode($data));
                }
                break;

            case 'uri':
                if (null === filter_var($data, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE)) {
                    $this->throwError('Invalid uri, '.json_encode($data));
                }
                break;

            default:
                $this->throwError('Unknown format, '.json_encode($data));
        }
    }

    protected function validateUnique($data)
    {
        $count = count($data);
        for ($i = 0; $i < $count; ++$i) {
            for ($j = $i + 1; $j < $count; ++$j) {
                if (Utils::equals($data[$i], $data[$j])) {
                    return false;
                }
            }
        }

        return true;
    }

    protected function match($regex, $string)
    {
         return preg_match('/'.$regex.'/', $string, $match);
    }

    protected function throwError($msg)
    {
        $path = $this->path ?: '#';
        $error = sprintf("Property: %s. Error: %s", $path, $msg);
        throw new ValidationException($error);
    }
}
<?php

namespace JohnStevenson\JsonWorks\Schema;

use \JohnStevenson\JsonWorks\Utils;

class Model
{
    public $data;
    /**
    * @var array
    */
    protected $references = array();

    public function __construct($input)
    {
        $this->data = Utils::dataCopy((object) $input, array($this, 'initCallback'));
        $this->resolveReferences();
    }

    public function find($schema, array $keys)
    {
        while (!empty($keys) && $schema) {
            $type = gettype($schema);
            $key = array_shift($keys);

            if ('array' === $type) {
                $key = (int) $key;
            }
            $schema = Utils::get($schema, $key);
        }

        return $schema;
    }

    public function initCallback($data)
    {
        if ($ref = Utils::get($data, '$ref')) {

            if (is_string($ref) && 0 === strpos($ref, '#')) {
                $this->references[$ref] = null;
            } else {
                throw new \RuntimeException('Invalid reference');
            }
        }

        return $data;
    }

    public function resolveCallback($data)
    {
        if ($ref = Utils::get($data, '$ref')) {
            $data = Utils::get($this->references, $ref);
        }

        return $data;
    }

    private function resolveReferences()
    {
        if (!empty($this->references)) {

            foreach (array_keys($this->references) as $ref) {
                $keys = Utils::pathDecode($ref);

                if ($schema = $this->find($this->data, $keys)) {
                    $this->references[$ref] = $schema;
                } else {
                    throw new \RuntimeException('Unable to find ref '.$ref);
                }
            }

            foreach ($this->references as $ref => $schema) {
                $this->references[$ref] = $this->resolve($schema);
            }

            $this->data = Utils::dataCopy($this->data, array($this, 'resolveCallback'));
            $this->references = array();
        }
    }

    private function resolve($schema, $parents = array())
    {
        $result = $schema;

        if ($ref = Utils::get($schema, '$ref')) {
            $refSchema = Utils::get($this->references, $ref);

            if (in_array($ref, $parents)) {
                throw new \RuntimeException('Circular reference to ref '.$ref);
            } elseif (Utils::get($refSchema, '$ref')) {
                $parents[] = $ref;
                $result = $this->resolve($refSchema, $parents);
            } else {
                $result = $refSchema;
            }
        }

        return $result;
    }
}
<?php

namespace JohnStevenson\JsonWorks\Schema;

class ValidationException extends \Exception
{
}
<?php

namespace JohnStevenson\JsonWorks\Schema;

class Validator
{
    public $error;

    public function check($data, $model, $lax = false)
    {
        $result = true;
        $this->error = '';
        $constraints = new Constraints($lax);

        try {
            $constraints->validate($data, $model->data);
        } catch (ValidationException $e) {
            $this->error = $e->getMessage();
            $result = false;
        }

        return $result;
    }
}
<?php

namespace JohnStevenson\JsonWorks;

class Utils
{
    public static function get($container, $key, $default = null)
    {
        $result = $default;

        if (is_object($container)) {
            $result = isset($container->$key) ? $container->$key : $default;
        } elseif (is_array($container)) {
            $result = isset($container[$key]) ? $container[$key] : $default;
        }

        return $result;
    }

    public static function checkType($type, $value)
    {
        $result = false;

        if ('number' === $type) {
            $result = is_float($value) || is_integer($value);
        } elseif ('boolean' === $type) {
            $result = is_bool($value);
        } elseif ('integer' === $type) {
            // Large integers may be stored as a float (Issue:1). Note that data
            // may have been truncated to fit a 64-bit PHP_MAX_INT
            $result = is_integer($value) || (is_float($value) && $value === floor($value));
        } elseif (function_exists($func = 'is_'.$type)) {
            $result = call_user_func($func, $value);
        }

        return $result;
    }

    public static function equals($var1, $var2)
    {
        $type1 = gettype($var1);
        $type2 = gettype($var2);

        if ('integer' === $type1 && 'double' === $type2) {
            $var1 = floatval($var1);
            $type1 = 'double';
        } elseif ('integer' === $type2 && 'double' === $type1) {
            $var2 = floatval($var2);
            $type2 = 'double';
        }

        if ($type1 !== $type2) {
            return false;
        }

        if ('object' === $type1) {
            return static::equalsObject($var1, $var2);
        } elseif ('array' === $type1) {
            return static::equalsArray($var1, $var2);
        } elseif ('double' === $type1) {
            return 0 === bccomp($var1, $var2, 16);
        } else {
            return $var1 === $var2;
        }
    }

    public static function uniqueArray($data, $check = false)
    {
        $out = array();
        $count = count($data);
        $equals = array();

        for ($i = 0; $i < $count; ++$i) {
            if (!in_array($i, $equals)) {
                $out[] = $data[$i];

                for ($j = $i + 1; $j < $count; ++$j) {
                    if (static::equals($data[$i], $data[$j])) {
                        $equals[] = $j;
                        if ($check) {
                            return false;
                        }
                    }
                }
            }
        }

        return $check ? true : $out;
    }

    public static function pathAdd($path, $key)
    {
        if (strlen($encoded = static::pathEncodeKey($key))) {
            $encoded = '/'.$encoded;
        }

        return $path.$encoded;
    }

    public static function pathDecode($path)
    {
        $keys = explode('/', $path);
        array_shift($keys);

        foreach ($keys as &$value) {
            $value = str_replace('~0', '~', str_replace('~1', '/', $value));
        }

        return $keys;
    }

    public static function pathEncode($keys)
    {
        $result = '';
        foreach ((array) $keys as $value) {
            $result = static::pathAdd($result, $value);
        }

        return $result;
    }

    public static function pathEncodeKey($key)
    {
        return str_replace('/', '~1', str_replace('~', '~0', strval($key)));
    }

    public static function dataCopy($data, $callback = null)
    {
        if ($callback) {
            $data = call_user_func_array($callback, array($data));
        }

        if (($object = is_object($data)) || is_array($data)) {

            $result = array();

            foreach ($data as $key => $value) {
                $object = $object ?: is_string($key);
                $result[$key] = static::dataCopy($value, $callback);
            }

            $result = $object ? (object) $result: $result;

        } else {
            $result = $data;
        }

        return $result;
    }

    public static function dataPrune($data)
    {
        $props = 0;
        return  static::workPrune($data, $props);
    }

    public static function dataOrder($data, $schema)
    {
        if (is_object($data) && ($properties = Utils::get($schema, 'properties'))) {
            $result = array();

            foreach ($properties as $key => $value) {
                if (isset($data->$key)) {
                    $result[$key] = static::dataOrder($data->$key, $properties->$key);
                    unset($data->$key);
                }
            }
            $result = (object) array_merge($result, (array) $data);

        } elseif (is_array($data) && ($items = Utils::get($schema, 'items'))) {
            $result = array();
            $objSchema = is_object($schema->items) ? $schema->items : null;

            foreach ($data as $item) {
                $itemSchema = $objSchema ?: (next($schema->items) ?: null);
                $result[] = static::dataOrder($item, $itemSchema);
            }

        } else {
            $result = $data;
        }

        return $result;
    }

    /**
    * Encodes data into JSON
    *
    * @param mixed $data The data to be encoded
    * @param boolean $pretty Format the output
    * @return string Encoded json
    */
    public static function dataToJson($data, $pretty)
    {
        $newLine = $pretty ? chr(10) : null;

        if (version_compare(PHP_VERSION, '5.4', '>=')) {
            $pprint = $pretty ? JSON_PRETTY_PRINT : 0;
            $options = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | $pprint;
            return static::finalizeJson(json_encode($data, $options), $newLine);
        }

        $json = json_encode($data);

        $len = strlen($json);
        $result = $string = '';
        $inString = $escaped = false;
        $level = 0;
        $space = $pretty ? chr(32) : null;
        $convert = function_exists('mb_convert_encoding');

        for ($i = 0; $i < $len; $i++) {
            $char = $json[$i];

            # are we inside a json string?
            if ('"' === $char && !$escaped) {
                $inString = !$inString;
            }

            if ($inString) {
                $string .= $char;
                $escaped = '\\' === $char ? !$escaped : false;

                continue;

            } elseif ($string) {
                # end of the json string
                $string .= $char;

                # unescape slashes
                $string = str_replace('\\/', '/', $string);

                # unescape unicode
                if ($convert) {
                    $string = preg_replace_callback('/\\\\u([0-9a-f]{4})/i', function ($match) {
                        return mb_convert_encoding(pack('H*', $match[1]), 'UTF-8', 'UCS-2BE');
                    }, $string);
                }

                $result .= $string;
                $string = '';

                continue;
            }

            if (':' === $char) {
                # add space after colon
                $char .= $space;
            } elseif (strpbrk($char, '}]')) {
                # char is an end element, so add a newline
                $result .= $newLine;
                # decrease indent level
                $level--;
                $result .= str_repeat($space, $level * 4);
            }

            $result .= $char;

            if (strpbrk($char, ',{[')) {
                # char is a start element, so add a newline
                $result .= $newLine;

                # increase indent level if not a comma
                if (',' !== $char) {
                    $level++;
                }

                $result .= str_repeat($space, $level * 4);
            }
        }

        return static::finalizeJson($result, $newLine);
    }

    protected static function equalsObject($obj1, $obj2)
    {
        # get_object_vars fails on objects with digit keys
        if (count((array) $obj1) !== count((array) $obj2)) {
            return false;
        }

        foreach ($obj1 as $key => $value) {
            if (!isset($obj2->$key) || !static::equals($value, $obj2->$key)) {
                return false;
            }
        }

        return true;
    }

    protected static function equalsArray($arr1, $arr2)
    {
        $count = count($arr1);

        if ($count !== count($arr2)) {
            return false;
        }

        for ($i = 0; $i < $count; ++$i) {
            if (!static::equals($arr1[$i], $arr2[$i])) {
                return false;
            }
        }

        return true;
    }

    protected static function workPrune($data, &$props)
    {
        if (($object = is_object($data)) || is_array($data)) {

            $result = array();
            $currentProps = $props;

            foreach ($data as $key => $value) {
                $object = $object ?: is_string($key);
                $value = static::workPrune($value, $props);

                if ($props > $currentProps) {
                    $result[$key] = $value;
                }
                $props = $currentProps;
            }

            $props = count($result);
            $result = $object ? (object) $result: $result;

        } else {
            ++$props;
            $result = $data;
        }

        return $result;
    }

    protected static function finalizeJson($json, $newline)
    {
        if ($newline) {
            # collapse empty {} and []
            $json = preg_replace_callback('#(\{\s+\})|(\[\s+\])#', function ($match) {
                return $match[1] ? '{}' : '[]';
            }, $json);

            $json .= $newline;
        }

        return $json;
    }
}
<?php

namespace DeepCopy;

use DeepCopy\Exception\CloneException;
use DeepCopy\Filter\Filter;
use DeepCopy\Matcher\Matcher;
use DeepCopy\TypeFilter\Spl\SplDoublyLinkedList;
use DeepCopy\TypeFilter\TypeFilter;
use DeepCopy\TypeMatcher\TypeMatcher;
use ReflectionProperty;
use DeepCopy\Reflection\ReflectionHelper;

/**
 * DeepCopy
 */
class DeepCopy
{
    /**
     * @var array
     */
    private $hashMap = [];

    /**
     * Filters to apply.
     * @var array
     */
    private $filters = [];

    /**
     * Type Filters to apply.
     * @var array
     */
    private $typeFilters = [];

    private $skipUncloneable = false;

    /**
     * @var bool
     */
    private $useCloneMethod;

    /**
     * @param bool $useCloneMethod   If set to true, when an object implements the __clone() function, it will be used
     *                               instead of the regular deep cloning.
     */
    public function __construct($useCloneMethod = false)
    {
        $this->useCloneMethod = $useCloneMethod;

        $this->addTypeFilter(new SplDoublyLinkedList($this), new TypeMatcher('\SplDoublyLinkedList'));
    }

    /**
     * Cloning uncloneable properties won't throw exception.
     * @param $skipUncloneable
     * @return $this
     */
    public function skipUncloneable($skipUncloneable = true)
    {
        $this->skipUncloneable = $skipUncloneable;
        return $this;
    }

    /**
     * Perform a deep copy of the object.
     * @param mixed $object
     * @return mixed
     */
    public function copy($object)
    {
        $this->hashMap = [];

        return $this->recursiveCopy($object);
    }

    public function addFilter(Filter $filter, Matcher $matcher)
    {
        $this->filters[] = [
            'matcher' => $matcher,
            'filter'  => $filter,
        ];
    }

    public function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher)
    {
        $this->typeFilters[] = [
            'matcher' => $matcher,
            'filter'  => $filter,
        ];
    }


    private function recursiveCopy($var)
    {
        // Matches Type Filter
        if ($filter = $this->getFirstMatchedTypeFilter($this->typeFilters, $var)) {
            return $filter->apply($var);
        }

        // Resource
        if (is_resource($var)) {
            return $var;
        }
        // Array
        if (is_array($var)) {
            return $this->copyArray($var);
        }
        // Scalar
        if (! is_object($var)) {
            return $var;
        }
        // Object
        return $this->copyObject($var);
    }

    /**
     * Copy an array
     * @param array $array
     * @return array
     */
    private function copyArray(array $array)
    {
        foreach ($array as $key => $value) {
            $array[$key] = $this->recursiveCopy($value);
        }

        return $array;
    }

    /**
     * Copy an object
     * @param object $object
     * @return object
     */
    private function copyObject($object)
    {
        $objectHash = spl_object_hash($object);

        if (isset($this->hashMap[$objectHash])) {
            return $this->hashMap[$objectHash];
        }

        $reflectedObject = new \ReflectionObject($object);

        if (false === $isCloneable = $reflectedObject->isCloneable() and $this->skipUncloneable) {
            $this->hashMap[$objectHash] = $object;
            return $object;
        }

        if (false === $isCloneable) {
            throw new CloneException(sprintf(
                'Class "%s" is not cloneable.',
                $reflectedObject->getName()
            ));
        }

        $newObject = clone $object;
        $this->hashMap[$objectHash] = $newObject;
        if ($this->useCloneMethod && $reflectedObject->hasMethod('__clone')) {
            return $object;
        }

        if ($newObject instanceof \DateTimeInterface) {
            return $newObject;
        }
        foreach (ReflectionHelper::getProperties($reflectedObject) as $property) {
            $this->copyObjectProperty($newObject, $property);
        }

        return $newObject;
    }

    private function copyObjectProperty($object, ReflectionProperty $property)
    {
        // Ignore static properties
        if ($property->isStatic()) {
            return;
        }

        // Apply the filters
        foreach ($this->filters as $item) {
            /** @var Matcher $matcher */
            $matcher = $item['matcher'];
            /** @var Filter $filter */
            $filter = $item['filter'];

            if ($matcher->matches($object, $property->getName())) {
                $filter->apply(
                    $object,
                    $property->getName(),
                    function ($object) {
                        return $this->recursiveCopy($object);
                    }
                );
                // If a filter matches, we stop processing this property
                return;
            }
        }

        $property->setAccessible(true);
        $propertyValue = $property->getValue($object);

        // Copy the property
        $property->setValue($object, $this->recursiveCopy($propertyValue));
    }

    /**
     * Returns first filter that matches variable, NULL if no such filter found.
     * @param array $filterRecords Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and
     *                             'matcher' with value of type {@see TypeMatcher}
     * @param mixed $var
     * @return TypeFilter|null
     */
    private function getFirstMatchedTypeFilter(array $filterRecords, $var)
    {
        $matched = $this->first(
            $filterRecords,
            function (array $record) use ($var) {
                /* @var TypeMatcher $matcher */
                $matcher = $record['matcher'];

                return $matcher->matches($var);
            }
        );

        return isset($matched) ? $matched['filter'] : null;
    }

    /**
     * Returns first element that matches predicate, NULL if no such element found.
     * @param array    $elements
     * @param callable $predicate Predicate arguments are: element.
     * @return mixed|null
     */
    private function first(array $elements, callable $predicate)
    {
        foreach ($elements as $element) {
            if (call_user_func($predicate, $element)) {
                return $element;
            }
        }

        return null;
    }
}
<?php
namespace DeepCopy\Exception;

class CloneException extends \UnexpectedValueException
{
} <?php

namespace DeepCopy\Filter\Doctrine;

use DeepCopy\Filter\Filter;
use ReflectionProperty;

/**
 * Set a null value for a property
 */
class DoctrineCollectionFilter implements Filter
{
    /**
     * {@inheritdoc}
     */
    public function apply($object, $property, $objectCopier)
    {
        $reflectionProperty = new ReflectionProperty($object, $property);

        $reflectionProperty->setAccessible(true);
        $oldCollection = $reflectionProperty->getValue($object);

        $newCollection = $oldCollection->map(
            function ($item) use ($objectCopier) {
                return $objectCopier($item);
            }
        );

        $reflectionProperty->setValue($object, $newCollection);
    }
}
<?php

namespace DeepCopy\Filter\Doctrine;

use DeepCopy\Filter\Filter;
use Doctrine\Common\Collections\ArrayCollection;

class DoctrineEmptyCollectionFilter implements Filter
{
    /**
     * Apply the filter to the object.
     *
     * @param object   $object
     * @param string   $property
     * @param callable $objectCopier
     */
    public function apply($object, $property, $objectCopier)
    {
        $reflectionProperty = new \ReflectionProperty($object, $property);
        $reflectionProperty->setAccessible(true);

        $reflectionProperty->setValue($object, new ArrayCollection());
    }
} <?php

namespace DeepCopy\Filter\Doctrine;

use DeepCopy\Filter\Filter;

/**
 * Trigger the magic method __load() on a Doctrine Proxy class to load the
 * actual entity from the database.
 */
class DoctrineProxyFilter implements Filter
{
    /**
     * {@inheritdoc}
     */
    public function apply($object, $property, $objectCopier)
    {
        $object->__load();
    }
}
<?php

namespace DeepCopy\Filter;

/**
 * Filter to apply to a property while copying an object
 */
interface Filter
{
    /**
     * Apply the filter to the object.
     * @param object   $object
     * @param string   $property
     * @param callable $objectCopier
     */
    public function apply($object, $property, $objectCopier);
}
<?php

namespace DeepCopy\Filter;

/**
 * Keep the value of a property
 */
class KeepFilter implements Filter
{
    /**
     * {@inheritdoc}
     */
    public function apply($object, $property, $objectCopier)
    {
        // Nothing to do
    }
}
<?php

namespace DeepCopy\Filter;

/**
 * Replace the value of a property
 */
class ReplaceFilter implements Filter
{
    /**
     * @var callable
     */
    protected $callback;

    /**
     * @param callable $callable Will be called to get the new value for each property to replace
     */
    public function __construct(callable $callable)
    {
        $this->callback = $callable;
    }

    /**
     * {@inheritdoc}
     */
    public function apply($object, $property, $objectCopier)
    {
        $reflectionProperty = new \ReflectionProperty($object, $property);
        $reflectionProperty->setAccessible(true);

        $value = call_user_func($this->callback, $reflectionProperty->getValue($object));

        $reflectionProperty->setValue($object, $value);
    }
}
<?php

namespace DeepCopy\Filter;

use ReflectionProperty;

/**
 * Set a null value for a property
 */
class SetNullFilter implements Filter
{
    /**
     * {@inheritdoc}
     */
    public function apply($object, $property, $objectCopier)
    {
        $reflectionProperty = new ReflectionProperty($object, $property);

        $reflectionProperty->setAccessible(true);
        $reflectionProperty->setValue($object, null);
    }
}
<?php

namespace DeepCopy\Matcher\Doctrine;

use DeepCopy\Matcher\Matcher;
use Doctrine\Common\Persistence\Proxy;

/**
 * Match a Doctrine Proxy class.
 */
class DoctrineProxyMatcher implements Matcher
{
    /**
     * {@inheritdoc}
     */
    public function matches($object, $property)
    {
        return $object instanceof Proxy;
    }
}
<?php

namespace DeepCopy\Matcher;

/**
 * Matcher interface
 */
interface Matcher
{
    /**
     * @param object $object
     * @param string $property
     * @return boolean
     */
    public function matches($object, $property);
}
<?php

namespace DeepCopy\Matcher;

/**
 * Match a specific property of a specific class
 */
class PropertyMatcher implements Matcher
{
    /**
     * @var string
     */
    private $class;

    /**
     * @var string
     */
    private $property;

    /**
     * @param string $class    Class name
     * @param string $property Property name
     */
    public function __construct($class, $property)
    {
        $this->class = $class;
        $this->property = $property;
    }

    /**
     * {@inheritdoc}
     */
    public function matches($object, $property)
    {
        return ($object instanceof $this->class) && ($property == $this->property);
    }
}
<?php

namespace DeepCopy\Matcher;

/**
 * Match a property by its name
 */
class PropertyNameMatcher implements Matcher
{
    /**
     * @var string
     */
    private $property;

    /**
     * @param string $property Property name
     */
    public function __construct($property)
    {
        $this->property = $property;
    }

    /**
     * {@inheritdoc}
     */
    public function matches($object, $property)
    {
        return $property == $this->property;
    }
}
<?php

namespace DeepCopy\Matcher;

use ReflectionProperty;

/**
 * Match a property by its type
 *
 * It is recommended to use {@see DeepCopy\TypeFilter\TypeFilter} instead, as it applies on all occurrences
 * of given type in copied context (eg. array elements), not just on object properties.
 */
class PropertyTypeMatcher implements Matcher
{
    /**
     * @var string
     */
    private $propertyType;

    /**
     * @param string $propertyType Property type
     */
    public function __construct($propertyType)
    {
        $this->propertyType = $propertyType;
    }

    /**
     * {@inheritdoc}
     */
    public function matches($object, $property)
    {
        $reflectionProperty = new ReflectionProperty($object, $property);
        $reflectionProperty->setAccessible(true);

        return $reflectionProperty->getValue($object) instanceof $this->propertyType;
    }
}
<?php

namespace DeepCopy\Reflection;

class ReflectionHelper
{
    /**
     * Retrieves all properties (including private ones), from object and all its ancestors.
     *
     * Standard \ReflectionClass->getProperties() does not return private properties from ancestor classes.
     *
     * @author muratyaman@gmail.com
     * @see http://php.net/manual/en/reflectionclass.getproperties.php
     *
     * @param \ReflectionClass $ref
     * @return \ReflectionProperty[]
     */
    public static function getProperties(\ReflectionClass $ref)
    {
        $props = $ref->getProperties();
        $propsArr = array();

        foreach ($props as $prop) {
            $propertyName = $prop->getName();
            $propsArr[$propertyName] = $prop;
        }

        if ($parentClass = $ref->getParentClass()) {
            $parentPropsArr = self::getProperties($parentClass);
            foreach ($propsArr as $key => $property) {
                $parentPropsArr[$key] = $property;
            }

            return $parentPropsArr;
        }

        return $propsArr;
    }
}
<?php

namespace DeepCopy\TypeFilter;

class ReplaceFilter implements TypeFilter
{
    /**
     * @var callable
     */
    protected $callback;

    /**
     * @param callable $callable Will be called to get the new value for each element to replace
     */
    public function __construct(callable $callable)
    {
        $this->callback = $callable;
    }

    /**
     * {@inheritdoc}
     */
    public function apply($element)
    {
        return call_user_func($this->callback, $element);
    }
}
<?php

namespace DeepCopy\TypeFilter;

class ShallowCopyFilter implements TypeFilter
{
    /**
     * {@inheritdoc}
     */
    public function apply($element)
    {
        return clone $element;
    }
}
<?php

namespace DeepCopy\TypeFilter\Spl;

use DeepCopy\DeepCopy;
use DeepCopy\TypeFilter\TypeFilter;

class SplDoublyLinkedList implements TypeFilter
{
    /**
     * @var DeepCopy
     */
    private $deepCopy;

    public function __construct(DeepCopy $deepCopy)
    {
        $this->deepCopy = $deepCopy;
    }

    /**
     * {@inheritdoc}
     */
    public function apply($element)
    {
        $newElement = clone $element;

        if ($element instanceof \SplDoublyLinkedList) {
            // Replace each element in the list with a deep copy of itself
            for ($i = 1; $i <= $newElement->count(); $i++) {
                $newElement->push($this->deepCopy->copy($newElement->shift()));
            }
        }

        return $newElement;

    }
}
<?php

namespace DeepCopy\TypeFilter;

interface TypeFilter
{
    /**
     * Apply the filter to the object.
     * @param mixed $element
     */
    public function apply($element);
}
<?php

namespace DeepCopy\TypeMatcher;

/**
 * TypeMatcher class
 */
class TypeMatcher
{
    /**
     * @var string
     */
    private $type;

    /**
     * @param string $type
     */
    public function __construct($type)
    {
        $this->type = $type;
    }

    /**
     * @param $element
     * @return boolean
     */
    public function matches($element)
    {
        return is_object($element) ? is_a($element, $this->type) : gettype($element) === $this->type;
    }
}
<?php

/**
 * This is the Phing command line launcher. It starts up the system evironment
 * tests for all important paths and properties and kicks of the main command-
 * line entry point of phing located in phing.Phing
 * @version $Id: e414c24b4fc34b7949bc762e9314750a83eea86b $
 */

// Use composers autoload.php if available
if (file_exists(dirname(__FILE__) . '/../vendor/autoload.php')) {
    require_once dirname(__FILE__) . '/../vendor/autoload.php';
} elseif (file_exists(dirname(__FILE__) . '/../../../autoload.php')) {
    require_once dirname(__FILE__) . '/../../../autoload.php';
}

// Set any INI options for PHP
// ---------------------------

/* set include paths */
set_include_path(
            dirname(__FILE__) . '/../classes' .
            PATH_SEPARATOR .
            get_include_path()
        );

require_once 'phing/Phing.php';

/**
* Code from Symfony/Component/Console/Output/StreamOutput.php
*/
function hasColorSupport()
{
    if (DIRECTORY_SEPARATOR == '\\') {
        return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI');
    }
    return function_exists('posix_isatty') && @posix_isatty(STDOUT);
}

// default logger
if (!in_array('-logger', $argv) && hasColorSupport()) {
    array_splice($argv, 1, 0, array('-logger', 'phing.listener.AnsiColorLogger'));
}

try {

    /* Setup Phing environment */
    Phing::startup();

    // Set phing.home property to the value from environment
    // (this may be NULL, but that's not a big problem.)
    Phing::setProperty('phing.home', getenv('PHING_HOME'));
    // Grab and clean up the CLI arguments
    $args = isset($argv) ? $argv : $_SERVER['argv']; // $_SERVER['argv'] seems to not work (sometimes?) when argv is registered
    array_shift($args); // 1st arg is script name, so drop it

    // Invoke the commandline entry point
    Phing::fire($args);

    // Invoke any shutdown routines.
    Phing::shutdown();

} catch (ConfigurationException $x) {

    Phing::printMessage($x);
    exit(-1); // This was convention previously for configuration errors.

} catch (Exception $x) {

    // Assume the message was already printed as part of the build and
    // exit with non-0 error code.

    exit(1);

}
<?php
/*
 *  $Id: 01f02b12b7d7946bcd16fd87c1a638fa3a724421 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/MatchingTask.php';
include_once 'phing/types/FileSet.php';
include_once 'phing/tasks/ext/pearpackage/Fileset.php';

/**
 *
 * @author   Hans Lellelid <hans@xmpl.org>
 * @package  phing.tasks.ext
 * @version  $Revision$
 */
class BuildPhingPEARPackageTask extends MatchingTask
{
    /** Base directory for reading files. */
    private $dir;

    private $version;
    private $state = 'stable';
    private $notes;

    private $mode = 'source';

    private $filesets = array();

    /** Package file */
    private $packageFile;

    public function init()
    {
        include_once 'PEAR/PackageFileManager2.php';
        if (!class_exists('PEAR_PackageFileManager2')) {
            throw new BuildException("You must have installed PEAR_PackageFileManager2 (PEAR_PackageFileManager >= 1.6.0) in order to create a PEAR package.xml file.");
        }
    }

    private function setOptions($pkg)
    {
        $options['baseinstalldir'] = 'phing';
        $options['packagedirectory'] = $this->dir->getAbsolutePath();

        if (empty($this->filesets)) {
            throw new BuildException("You must use a <fileset> tag to specify the files to include in the package.xml");
        }

        $options['filelistgenerator'] = 'Fileset';

        // Some PHING-specific options needed by our Fileset reader
        $options['phing_project'] = $this->getProject();
        $options['phing_filesets'] = $this->filesets;

        if ($this->packageFile !== null) {
            // create one w/ full path
            $f = new PhingFile($this->packageFile->getAbsolutePath());
            $options['packagefile'] = $f->getName();
            // must end in trailing slash
            $options['outputdirectory'] = $f->getParent() . DIRECTORY_SEPARATOR;
            $this->log("Creating package file: " . $f->getPath(), Project::MSG_INFO);
        } else {
            $this->log("Creating [default] package.xml file in base directory.", Project::MSG_INFO);
        }

        if ($this->mode == "docs") {
            $options['dir_roles'] = array(  'phing_guide' => 'doc',
                                            'api' => 'doc',
                                            'example' => 'doc');
        } else {
            // add install exceptions
            $options['installexceptions'] = array(  'bin/phing.php' => '/',
                                                    'bin/pear-phing' => '/',
                                                    'bin/pear-phing.bat' => '/',
                                                    );

            $options['dir_roles'] = array(  'etc' => 'data');

            $options['exceptions'] = array( 'bin/pear-phing.bat' => 'script',
                                            'bin/pear-phing' => 'script',
                                            'CREDITS.md' => 'doc',
                                            'CHANGELOG.md' => 'doc',
                                            'LICENSE' => 'doc',
                                            'README.md' => 'doc');
        }

        $pkg->setOptions($options);

    }

    /**
     * Main entry point.
     * @return void
     */
    public function main()
    {
        if ($this->dir === null) {
            throw new BuildException("You must specify the \"dir\" attribute for PEAR package task.");
        }

        if ($this->version === null) {
            throw new BuildException("You must specify the \"version\" attribute for PEAR package task.");
        }

        $package = new PEAR_PackageFileManager2();

        $this->setOptions($package);

        // the hard-coded stuff
        if ($this->mode == "docs") {
            $package->setPackage('phingdocs');
            $package->setSummary('PHP5 project build system based on Apache Ant (documentation)');
        } else {
            $package->setPackage('phing');
            $package->setSummary('PHP5 project build system based on Apache Ant');
        }

        $package->setDescription('PHing Is Not GNU make; it\'s a project build system based on Apache Ant.
You can do anything with it that you could do with a traditional build system like GNU make, and its use of
simple XML build files and extensible PHP "task" classes make it an easy-to-use and highly flexible build framework.
Features include file transformations (e.g. token replacement, XSLT transformation, Smarty template transformations,
etc.), file system operations, interactive build support, SQL execution, and much more.');
        $package->setChannel('pear.phing.info');
        $package->setPackageType('php');

        $package->setReleaseVersion($this->version);
        $package->setAPIVersion($this->version);

        $package->setReleaseStability($this->state);
        $package->setAPIStability($this->state);

        $package->setNotes($this->notes);

        $package->setLicense('LGPL', 'http://www.gnu.org/licenses/lgpl.html');

        // Add package maintainers
        $package->addMaintainer('lead', 'mrook', 'Michiel Rook', 'mrook@php.net');

        // (wow ... this is a poor design ...)
        //
        // note that the order of the method calls below is creating
        // sub-"release" sections which have specific rules.  This replaces
        // the platformexceptions system in the older version of PEAR's package.xml
        //
        // Programmatically, I feel the need to re-iterate that this API for PEAR_PackageFileManager
        // seems really wrong.  Sub-sections should be encapsulated in objects instead of having
        // a "flat" API that does not represent the structure being created....

        if ($this->mode != "docs") {
            // creating a sub-section for 'windows'
            $package->addRelease();
            $package->setOSInstallCondition('windows');
            $package->addInstallAs('bin/phing.php', 'phing.php');
            $package->addInstallAs('bin/pear-phing.bat', 'phing.bat');
            $package->addIgnoreToRelease('bin/pear-phing');

            // creating a sub-section for non-windows
            $package->addRelease();
            $package->addInstallAs('bin/phing.php', 'phing.php');
            $package->addInstallAs('bin/pear-phing', 'phing');
            $package->addIgnoreToRelease('bin/pear-phing.bat');
        }


        // "core" dependencies
        $package->setPhpDep('5.2.0');
        $package->setPearinstallerDep('1.8.0');

        // "package" dependencies
        if ($this->mode != "docs") {
            $package->addPackageDepWithChannel( 'optional', 'phingdocs', 'pear.phing.info', $this->version);
            $package->addPackageDepWithChannel( 'optional', 'VersionControl_SVN', 'pear.php.net', '0.4.0');
            $package->addPackageDepWithChannel( 'optional', 'VersionControl_Git', 'pear.php.net', '0.4.3');
            $package->addPackageDepWithChannel( 'optional', 'Xdebug', 'pecl.php.net', '2.0.5');
            $package->addPackageDepWithChannel( 'optional', 'Archive_Tar', 'pear.php.net', '1.3.8');
            $package->addPackageDepWithChannel( 'optional', 'PEAR_PackageFileManager', 'pear.php.net', '1.5.2');
            $package->addPackageDepWithChannel( 'optional', 'Services_Amazon_S3', 'pear.php.net', '0.3.1');
            $package->addPackageDepWithChannel( 'optional', 'HTTP_Request2', 'pear.php.net', '2.1.1');
            $package->addPackageDepWithChannel( 'optional', 'PHP_Depend', 'pear.pdepend.org', '0.10.0');
            $package->addPackageDepWithChannel( 'optional', 'PHP_PMD', 'pear.phpmd.org', '1.1.0');
            $package->addPackageDepWithChannel( 'optional', 'phpDocumentor', 'pear.phpdoc.org', '2.0.0b7');
            $package->addPackageDepWithChannel( 'optional', 'PHP_CodeSniffer', 'pear.php.net', '1.5.0');
            $package->addPackageDepWithChannel( 'optional', 'Net_Growl', 'pear.php.net', '2.6.0');

            // now add the replacements, chdir() to source directory
            // to allow addReplacement() to find the specified files
            $cwd = getcwd();
            chdir($this->dir->getAbsolutePath());

            $package->addReplacement('Phing.php', 'pear-config', '@DATA-DIR@', 'data_dir');
            $package->addReplacement('bin/pear-phing.bat', 'pear-config', '@PHP-BIN@', 'php_bin');
            $package->addReplacement('bin/pear-phing.bat', 'pear-config', '@BIN-DIR@', 'bin_dir');
            $package->addReplacement('bin/pear-phing.bat', 'pear-config', '@PEAR-DIR@', 'php_dir');
            $package->addReplacement('bin/pear-phing', 'pear-config', '@PHP-BIN@', 'php_bin');
            $package->addReplacement('bin/pear-phing', 'pear-config', '@BIN-DIR@', 'bin_dir');
            $package->addReplacement('bin/pear-phing', 'pear-config', '@PEAR-DIR@', 'php_dir');

            chdir($cwd);
        }

        $package->generateContents();

        $e = $package->writePackageFile();

        if (PEAR::isError($e)) {
            throw new BuildException("Unable to write package file.", new Exception($e->getMessage()));
        }

    }

    /**
     * Used by the PEAR_PackageFileManager_PhingFileSet lister.
     * @return array FileSet[]
     */
    public function getFileSets()
    {
        return $this->filesets;
    }

    // -------------------------------
    // Set properties from XML
    // -------------------------------

    /**
     * Nested creator, creates a FileSet for this task
     *
     * @return FileSet The created fileset object
     */
    public function createFileSet()
    {
        $num = array_push($this->filesets, new FileSet());

        return $this->filesets[$num-1];
    }

    /**
     * Set the version we are building.
     * @param  string $v
     * @return void
     */
    public function setVersion($v)
    {
        $this->version = $v;
    }

    /**
     * Set the state we are building.
     * @param  string $v
     * @return void
     */
    public function setState($v)
    {
        $this->state = $v;
    }

    /**
     * Sets release notes field.
     * @param  string $v
     * @return void
     */
    public function setNotes($v)
    {
        $this->notes = $v;
    }
    /**
     * Sets "dir" property from XML.
     * @param  PhingFile $f
     * @return void
     */
    public function setDir(PhingFile $f)
    {
        $this->dir = $f;
    }

    /**
     * Sets the file to use for generated package.xml
     */
    public function setDestFile(PhingFile $f)
    {
        $this->packageFile = $f;
    }

    /**
     * Sets mode property
     * @param  string $v
     * @return void
     */
    public function setMode($v)
    {
        $this->mode = $v;
    }
}
#!/usr/bin/env php
<?php

try {
    Phar::mapPhar('phing.phar');
    include 'phar://phing.phar/bin/phing.php';
} catch (PharException $e) {
    echo $e->getMessage();
    echo 'Cannot initialize Phar';
    exit(1);
}

__HALT_COMPILER();
<?php
/*
 *  $Id: f406bad78fbfd0f905a64e9f8b1933356ecb87b6 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/lang/EventObject.php';

/**
 * Encapsulates a build specific event.
 *
 * <p>We have three sources of events all handled by this class:
 *
 * <ul>
 *  <li>Project level events</li>
 *  <li>Target level events</li>
 *  <li>Task level events</li>
 * </ul>
 *
 * <p> Events are all fired from the project class by creating an event object
 * using this class and passing it to the listeners.
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: f406bad78fbfd0f905a64e9f8b1933356ecb87b6 $
 * @package   phing
 */
class BuildEvent extends EventObject
{

    /**
     * A reference to the project
     * @var Project
     */
    protected $project;

    /**
     * A reference to the target
     * @var Target
     */
    protected $target;

    /**
     * A reference to the task
     *
     * @var Task
     */
    protected $task;

    /**
     * The message of this event, if the event is a message
     * @var string
     */
    protected $message = null;

    /**
     * The priority of the message
     *
     * @var    string
     * @see    $message
     */
    protected $priority = Project::MSG_VERBOSE;

    /**
     * The exception that caused the event, if any
     *
     * @var    object
     */
    protected $exception = null;

    /**
     * Construct a BuildEvent for a project, task or target source event
     *
     * @param Project|Target|Task $source
     *
     * @throws Exception
     */
    public function __construct($source)
    {
        parent::__construct($source);
        if ($source instanceof Project) {
            $this->project = $source;
            $this->target = null;
            $this->task = null;
        } elseif ($source instanceof Target) {
            $this->project = $source->getProject();
            $this->target = $source;
            $this->task = null;
        } elseif ($source instanceof Task) {
            $this->project = $source->getProject();
            $this->target = $source->getOwningTarget();
            $this->task = $source;
        } else {
            throw new Exception("Can not construct BuildEvent, unknown source given.");
        }
    }

    /**
     * Sets the message with details and the message priority for this event.
     *
     * @param  string   The string message of the event
     * @param  integer  The priority this message should have
     */
    public function setMessage($message, $priority)
    {
        $this->message = (string) $message;
        $this->priority = (int) $priority;
    }

    /**
     * Set the exception that was the cause of this event.
     *
     * @param  Exception The exception that caused the event
     */
    public function setException($exception)
    {
        $this->exception = $exception;
    }

    /**
     * Returns the project instance that fired this event.
     *
     * The reference to the project instance is set by the constructor if this
     * event was fired from the project class.
     *
     * @return Project The project instance that fired this event
     */
    public function getProject()
    {
        return $this->project;
    }

    /**
     * Returns the target instance that fired this event.
     *
     * The reference to the target instance is set by the constructor if this
     * event was fired from the target class.
     *
     * @return Target The target that fired this event
     */
    public function getTarget()
    {
        return $this->target;
    }

    /**
     * Returns the target instance that fired this event.
     *
     * The reference to the task instance is set by the constructor if this
     * event was fired within a task.
     *
     * @return Task The task that fired this event
     */
    public function getTask()
    {
        return $this->task;
    }

    /**
     * Returns the logging message. This field will only be set for
     * "messageLogged" events.
     *
     * @return string The log message
     */
    public function getMessage()
    {
        return $this->message;
    }

    /**
     * Returns the priority of the logging message. This field will only
     * be set for "messageLogged" events.
     *
     * @return integer The message priority
     */
    public function getPriority()
    {
        return $this->priority;
    }

    /**
     * Returns the exception that was thrown, if any.
     * This field will only be set for "taskFinished", "targetFinished", and
     * "buildFinished" events.
     *
     * @see BuildListener::taskFinished()
     * @see BuildListener::targetFinished()
     * @see BuildListener::buildFinished()
     * @return Exception
     */
    public function getException()
    {
        return $this->exception;
    }
}
<?php
/**
 *  $Id: b20e9c95f1db91e9c0ec65ff977bfb22f0308601 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * BuildException is for when things go wrong in a build execution.
 *
 * @author   Andreas Aderhold <andi@binarycloud.com>
 * @version  $Id: b20e9c95f1db91e9c0ec65ff977bfb22f0308601 $
 * @package  phing
 */
class BuildException extends RuntimeException
{

    /**
     * Location in the xml file.
     * @var Location
     */
    protected $location;

    /**
     * The nested "cause" exception.
     * @var Exception
     */
    protected $cause;

    /**
     * Construct a BuildException.
     * Supported signatures:
     *         throw new BuildException($causeExc);
     *         throw new BuildException($msg);
     *         throw new Buildexception($causeExc, $loc);
     *         throw new BuildException($msg, $causeExc);
     *         throw new BuildException($msg, $loc);
     *         throw new BuildException($msg, $causeExc, $loc);
     * @param Exception|string        $p1
     * @param Location|Exception|null $p2
     * @param Location|null           $p3
     */
    public function __construct($p1 = "", $p2 = null, $p3 = null)
    {

        $cause = null;
        $loc = null;
        $msg = "";

        if ($p3 !== null) {
            $cause = $p2;
            $loc = $p3;
            $msg = $p1;
        } elseif ($p2 !== null) {
            if ($p2 instanceof Exception) {
                $cause = $p2;
                $msg = $p1;
            } elseif ($p2 instanceof Location) {
                $loc = $p2;
                if ($p1 instanceof Exception) {
                    $cause = $p1;
                } else {
                    $msg = $p1;
                }
            }
        } elseif ($p1 instanceof Exception) {
            $cause = $p1;
        } else {
            $msg = $p1;
        }

        parent::__construct($msg);

        if ($cause !== null) {
            $this->cause = $cause;
            $this->message .= "\n" . $this->getTraceAsString();
            $this->message .= "\n\nPrevious " . (string) $cause;
        }

        if ($loc !== null) {
            $this->setLocation($loc);
        }
    }

    /**
     * Gets the cause exception.
     *
     * @return Exception
     */
    public function getCause()
    {
        return $this->cause;
    }

    /**
     * Gets the location of error in XML file.
     *
     * @return Location
     */
    public function getLocation()
    {
        return $this->location;
    }

    /**
     * Sets the location of error in XML file.
     *
     * @param Location $loc
     */
    public function setLocation(Location $loc)
    {
        $this->location = $loc;
        $this->message = $loc->toString() . ': ' . $this->message;
    }
}
<?php
/*
 *  $Id: e0f8f926fe954fa2128283a0707dda812fcf1708 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Interface for build listeners.
 *
 * Classes that implement a listener must extend this class and (faux)implement
 * all methods that are decleard as dummies below.
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: e0f8f926fe954fa2128283a0707dda812fcf1708 $
 * @see       BuildEvent
 * @see       Project::addBuildListener()
 * @package   phing
 */
interface BuildListener
{

    /**
     * Fired before any targets are started.
     *
     * @param BuildEvent $event The BuildEvent
     */
    public function buildStarted(BuildEvent $event);

    /**
     * Fired after the last target has finished.
     *
     * @param BuildEvent $event The BuildEvent
     * @see BuildEvent::getException()
     */
    public function buildFinished(BuildEvent $event);

    /**
     * Fired when a target is started.
     *
     * @param BuildEvent $event The BuildEvent
     * @see BuildEvent::getTarget()
     */
    public function targetStarted(BuildEvent $event);

    /**
     * Fired when a target has finished.
     *
     * @param BuildEvent $event The BuildEvent
     * @see BuildEvent#getException()
     */
    public function targetFinished(BuildEvent $event);

    /**
     * Fired when a task is started.
     *
     * @param BuildEvent $event The BuildEvent
     * @see BuildEvent::getTask()
     */
    public function taskStarted(BuildEvent $event);

    /**
     * Fired when a task has finished.
     *
     * @param BuildEvent $event The BuildEvent
     * @see BuildEvent::getException()
     */
    public function taskFinished(BuildEvent $event);

    /**
     * Fired whenever a message is logged.
     *
     * @param BuildEvent $event The BuildEvent
     * @see BuildEvent::getMessage()
     */
    public function messageLogged(BuildEvent $event);
}
<?php
/*
 *  $Id: 695c190fb6a2339063b67d1557a6af553e6ef936 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/BuildListener.php';

/**
 * Interface for build loggers.
 *
 * Build loggers are build listeners but with some additional functionality:
 *   - They can be configured with a log level (below which they will ignore messages)
 *   - They have error and output streams
 *
 * Classes that implement a listener must implement this interface.
 *
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: 695c190fb6a2339063b67d1557a6af553e6ef936 $
 * @see       BuildEvent
 * @see       Project::addBuildListener()
 * @package   phing
 */
interface BuildLogger extends BuildListener
{

    /**
     * Sets the min log level that this logger should respect.
     *
     * Messages below this level are ignored.
     *
     * Constants for the message levels are in Project.php. The order of
     * the levels, from least to most verbose, is:
     *   - Project::MSG_ERR
     *   - Project::MSG_WARN
     *   - Project::MSG_INFO
     *   - Project::MSG_VERBOSE
     *   - Project::MSG_DEBUG
     *
     * @param int $level The log level integer (e.g. Project::MSG_VERBOSE, etc.).
     */
    public function setMessageOutputLevel($level);

    /**
     * Sets the standard output stream to use.
     * @param OutputStream $output Configured output stream (e.g. STDOUT) for standard output.
     */
    public function setOutputStream(OutputStream $output);

    /**
     * Sets the output stream to use for errors.
     * @param OutputStream $err Configured output stream (e.g. STDERR) for errors.
     */
    public function setErrorStream(OutputStream $err);

    /**
     * Sets this logger to produce emacs (and other editor) friendly output.
     *
     * @param bool $emacsMode true if output is to be unadorned so that emacs and other editors
     *                             can parse files names, etc.
     */
    public function setEmacsMode($emacsMode);
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * This exception is used to indicate timeouts.
 *
 * @author   Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package  phing
 */
class BuildTimeoutException extends BuildException
{
}
<?php
/*
 *  $Id: f5672973a0e8f94bc824729aed7285130e4322a3 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * ConfigurationException is thrown by Phing during the configuration and setup phase of the project.
 *
 * @author   Hans Lellelid <hans@xmpl.org>
 * @version  $Id: f5672973a0e8f94bc824729aed7285130e4322a3 $
 * @package  phing
 */
class ConfigurationException extends Exception
{

    /**
     * Location in the xml file.
     * @var Location
     */
    protected $location;

    /**
     * The nested "cause" exception.
     * @var Exception
     */
    protected $cause;

    /**
     * Construct a ConfigurationException.
     * Supported signatures:
     *         throw new BuildException($causeExc);
     *         throw new BuildException($msg);
     *         throw new BuildException($msg, $causeExc);
     * @param Exception|string $p1
     * @param Exception|null   $p2
     */
    public function __construct($p1, $p2 = null)
    {

        $cause = null;
        $msg = "";

        if ($p2 !== null) {
            if ($p2 instanceof Exception) {
                $cause = $p2;
                $msg = $p1;
            }
        } elseif ($p1 instanceof Exception) {
            $cause = $p1;
        } else {
            $msg = $p1;
        }

        parent::__construct($msg);

        if ($cause !== null) {
            $this->cause = $cause;
            $this->message .= " [wrapped: " . $cause->getMessage() . "]";
        }
    }

    /**
     * Gets the cause exception.
     *
     * @return Exception
     */
    public function getCause()
    {
        return $this->cause;
    }

}
<?php
/**
 * DocBlox
 *
 * PHP Version 5
 *
 * @category  DocBlox
 * @package   Parallel
 * @author    Mike van Riel <mike.vanriel@naenius.com>
 * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://docblox-project.org
 */

/**
 * Manager class for Parallel processes.
 *
 * This class will manage the workers and make sure all processes are executed
 * in parallel and not too many at the same time.
 *
 * @category DocBlox
 * @package  Parallel
 * @author   Mike van Riel <mike.vanriel@naenius.com>
 * @license  http://www.opensource.org/licenses/mit-license.php MIT
 * @link     http://docblox-project.org
 */
class DocBlox_Parallel_Manager extends ArrayObject
{
    /** @var int The maximum number of processes to run simultaneously */
    protected $process_limit = 2;

    /** @var boolean Tracks whether this manager is currently executing */
    protected $is_running = false;

    /**
     * Tries to autodetect the optimal number of process by counting the number
     * of processors.
     *
     * @param array  $input          Input for the array object.
     * @param int    $flags          flags for the array object.
     * @param string $iterator_class Iterator class for this array object.
     */
    public function __construct(
        $input = array(),
        $flags = 0,
        $iterator_class = "ArrayIterator"
    ) {
        parent::__construct($input, $flags, $iterator_class);

        if (is_readable('/proc/cpuinfo')) {
            $processors = 0;
            exec("cat /proc/cpuinfo | grep processor | wc -l", $processors);
            $this->setProcessLimit(reset($processors));
        }
    }

    /**
     * Adds a worker to the queue.
     *
     * This method will prepare a worker to be executed in parallel once the
     * execute method is invoked.
     * A fluent interface is provided so that you can chain multiple workers
     * in one call.
     *
     * Example:
     *
     *    $cb1 = function () { var_dump('a'); sleep(1); };
     *    $cb2 = function () { var_dump('b'); sleep(1); };
     *
     *    $mgr = new DocBlox_Parallel_Manager();
     *    $mgr->setProcessLimit(2)
     *        ->addWorker(new DocBlox_Parallel_Worker($cb1))
     *        ->addWorker(new DocBlox_Parallel_Worker($cb2))
     *        ->execute();
     *
     * @param int                     $index  The key for this worker.
     * @param DocBlox_Parallel_Worker $newval The worker to add onto the queue.
     *
     * @see DocBlox_Parallel_Manager::execute()
     *
     * @throws RuntimeException         if this method is invoked while the
     *                                  manager is busy executing tasks.
     * @throws InvalidArgumentException if the provided element is not of type
     *                                  DocBlox_Parallel_Worker.
     *
     * @return void
     */
    public function offsetSet($index, $newval)
    {
        if (!$newval instanceof DocBlox_Parallel_Worker) {
            throw new InvalidArgumentException(
                'Provided element must be of type DocBlox_Parallel_Worker'
            );
        }
        if ($this->isRunning()) {
            throw new RuntimeException(
                'Workers may not be added during execution of the manager'
            );
        }

        parent::offsetSet($index, $newval);
    }

    /**
     * Convenience method to make the addition of workers explicit and allow a
     * fluent interface.
     *
     * @param DocBlox_Parallel_Worker $worker The worker to add onto the queue.
     *
     * @return self
     */
    public function addWorker(DocBlox_Parallel_Worker $worker)
    {
        $this[] = $worker;

        return $this;
    }

    /**
     * Sets how many processes at most to execute at the same time.
     *
     * A fluent interface is provided so that you can chain multiple workers
     * in one call.
     *
     * @param int $process_limit The limit, minimum of 1
     *
     * @see DocBlox_Parallel_Manager::addWorker() for an example
     *
     * @return self
     */
    public function setProcessLimit($process_limit)
    {
        if ($process_limit < 1) {
            throw new InvalidArgumentException(
                'Number of simultaneous processes may not be less than 1'
            );
        }

        $this->process_limit = $process_limit;

        return $this;
    }

    /**
     * Returns the current limit on the amount of processes that can be
     * executed at the same time.
     *
     * @return int
     */
    public function getProcessLimit()
    {
        return $this->process_limit;
    }

    /**
     * Returns whether the manager is executing the workers.
     *
     * @return boolean
     */
    public function isRunning()
    {
        return $this->is_running;
    }

    /**
     * Executes each worker.
     *
     * This method loops through the list of workers and tries to fork as
     * many times as the ProcessLimit dictates at the same time.
     *
     * @return void
     */
    public function execute()
    {
        /** @var int[] $processes */
        $processes = $this->startExecution();

        /** @var DocBlox_Parallel_Worker $worker */
        foreach ($this as $worker) {

            // if requirements are not met, execute workers in series.
            if (!$this->checkRequirements()) {
                $worker->execute();
                continue;
            }

            $this->forkAndRun($worker, $processes);
        }

        $this->stopExecution($processes);
    }

    /**
     * Notifies manager that execution has started, checks requirements and
     * returns array for child processes.
     *
     * If forking is not available because library requirements are not met
     * than the list of workers is processed in series and a E_USER_NOTICE is
     * triggered.
     *
     * @return int[]
     */
    protected function startExecution()
    {
        $this->is_running = true;

        // throw a E_USER_NOTICE if the requirements are not met.
        if (!$this->checkRequirements()) {
            trigger_error(
                'The PCNTL extension is not available, running workers in series '
                . 'instead of parallel',
                E_USER_NOTICE
            );
        }

        return array();
    }

    /**
     * Waits for all processes to have finished and notifies the manager that
     * execution has stopped.
     *
     * @param int[] &$processes List of running processes.
     *
     * @return void
     */
    protected function stopExecution(array &$processes)
    {
        // starting of processes has ended but some processes might still be
        // running wait for them to finish
        while (!empty($processes)) {
            pcntl_waitpid(array_shift($processes), $status);
        }

        /** @var DocBlox_Parallel_Worker $worker */
        foreach ($this as $worker) {
            $worker->pipe->push();
        }

        $this->is_running = false;
    }

    /**
     * Forks the current process and calls the Worker's execute method OR
     * handles the parent process' execution.
     *
     * This is the really tricky part of the forking mechanism. Here we invoke
     * {@link http://www.php.net/manual/en/function.pcntl-fork.php pcntl_fork}
     * and either execute the forked process or deal with the parent's process
     * based on in which process we are.
     *
     * To fully understand what is going on here it is recommended to read the
     * PHP manual page on
     * {@link http://www.php.net/manual/en/function.pcntl-fork.php pcntl_fork}
     * and associated articles.
     *
     * If there are more workers than may be ran simultaneously then this method
     * will wait until a slot becomes available and then starts the next worker.
     *
     * @param DocBlox_Parallel_Worker $worker     The worker to process.
     * @param int[]                   &$processes The list of running processes.
     *
     * @throws RuntimeException if we are unable to fork.
     *
     * @return void
     */
    protected function forkAndRun(
        DocBlox_Parallel_Worker $worker,
        array &$processes
    ) {
        $worker->pipe = new DocBlox_Parallel_WorkerPipe($worker);

        // fork the process and register the PID
        $pid = pcntl_fork();

        switch ($pid) {
            case -1:
                throw new RuntimeException('Unable to establish a fork');
            case 0: // Child process
                $worker->execute();

                $worker->pipe->pull();

                // Kill -9 this process to prevent closing of shared file handlers.
                // Not doing this causes, for example, MySQL connections to be cleaned.
                posix_kill(getmypid(), SIGKILL);
            default: // Parent process
                // Keep track if the worker children
                $processes[] = $pid;

                if (count($processes) >= $this->getProcessLimit()) {
                    pcntl_waitpid(array_shift($processes), $status);
                }
                break;
        }
    }

    /**
     * Returns true when all requirements are met.
     *
     * @return bool
     */
    protected function checkRequirements()
    {
        return (bool) (extension_loaded('pcntl'));
    }
}
<?php
/**
 * DocBlox
 *
 * PHP Version 5
 *
 * @category  DocBlox
 * @package   Parallel
 * @author    Mike van Riel <mike.vanriel@naenius.com>
 * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://docblox-project.org
 */

/**
 * Class that represents the execution of a single task within a parallelized
 * frame.
 *
 * @category DocBlox
 * @package  Parallel
 * @author   Mike van Riel <mike.vanriel@naenius.com>
 * @license  http://www.opensource.org/licenses/mit-license.php MIT
 * @link     http://docblox-project.org
 */
class DocBlox_Parallel_Worker
{
    /** @var callback the task to execute for this worker */
    protected $task = null;

    /** @var mixed[] A list of argument to pass to the task */
    protected $arguments = array();

    /** @var int The return code to tell the parent process how it went */
    protected $return_code = -1;

    /** @var mixed The result of the given task */
    protected $result = '';

    /** @var string The error message, if an error occurred */
    protected $error = '';

    /**
     * Creates the worker and sets the task to execute optionally including
     * the arguments that need to be passed to the task.
     *
     * @param callback $task      The task to invoke upon execution.
     * @param mixed[]  $arguments The arguments to provide to the task.
     */
    public function __construct($task, array $arguments = array())
    {
        $this->setTask($task);
        $this->arguments = $arguments;
    }

    /**
     * Returns the list of arguments as provided in the constructor.
     *
     * @see DocBlox_Parallel_Worker::__construct()
     *
     * @return mixed[]
     */
    public function getArguments()
    {
        return $this->arguments;
    }

    /**
     * Returns the task as provided in the constructor.
     *
     * @see DocBlox_Parallel_Worker::__construct()
     *
     * @return callback
     */
    public function getTask()
    {
        return $this->task;
    }

    /**
     * Returns the available return code.
     *
     * This method may return -1 if no return code is available yet.
     *
     * @return int
     */
    public function getReturnCode()
    {
        return $this->return_code;
    }

    /**
     * Sets the return code for this worker.
     *
     * Recommended is to use the same codes as are used with
     * {@link http://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
     * exit codes}.
     *
     * In short: 0 means that the task succeeded and a any other positive value
     * indicates an error condition.
     *
     * @param int $return_code Recommended to be a positive number
     *
     * @throw InvalidArgumentException if the code is not a number or negative
     *
     * @return void
     */
    public function setReturnCode($return_code)
    {
        if (!is_numeric($return_code) || ($return_code < 0)) {
            throw new InvalidArgumentException(
                'Expected the return code to be a positive number'
            );
        }

        $this->return_code = $return_code;
    }

    /**
     * Returns the error message associated with the return code.
     *
     * @return string
     */
    public function getError()
    {
        return $this->error;
    }

    /**
     * Sets the error message.
     *
     * @param string $error The error message.
     *
     * @return void
     */
    public function setError($error)
    {
        $this->error = $error;
    }

    /**
     * Returns the result for this task run.
     *
     * @return null|mixed
     */
    public function getResult()
    {
        return $this->result;
    }

    /**
     * Sets the result for this task run.
     *
     * @param mixed $result The value that is returned by the task; can be anything.
     *
     * @return void
     */
    public function setResult($result)
    {
        $this->result = $result;
    }

    /**
     * Invokes the task with the given arguments and processes the output.
     *
     * @return void.
     */
    public function execute()
    {
        $this->setReturnCode(0);
        try {
            $this->setResult(
                call_user_func_array($this->getTask(), $this->getArguments())
            );
        } catch (Exception $e) {
            $this->setError($e->getMessage());
            $this->setReturnCode($e->getCode());
        }
    }

    /**
     * Sets the task for this worker and validates whether it is callable.
     *
     * @param callback $task The task to execute when the execute method
     *                       is invoked.
     *
     * @throws InvalidArgumentException if the given argument is not a callback.
     *
     * @see DocBlox_Parallel_Worker::__construct()
     * @see DocBlox_Parallel_Worker::execute()
     *
     * @return void
     */
    protected function setTask($task)
    {
        if (!is_callable($task)) {
            throw new InvalidArgumentException(
                'Worker task is not a callable object'
            );
        }

        $this->task = $task;
    }
}
<?php
/**
 * DocBlox
 *
 * PHP Version 5
 *
 * @category  DocBlox
 * @package   Parallel
 * @author    Mike van Riel <mike.vanriel@naenius.com>
 * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://docblox-project.org
 */

/**
 * Class that represents a named pipe for a Worker.
 *
 * This class manages the named pipe for a worker and is able to push and pull
 * specific data to facilitate IPC (interprocess communication).
 *
 * @category DocBlox
 * @package  Parallel
 * @author   Mike van Riel <mike.vanriel@naenius.com>
 * @license  http://www.opensource.org/licenses/mit-license.php MIT
 * @link     http://docblox-project.org
 */
class DocBlox_Parallel_WorkerPipe
{
    /** @var DocBlox_Parallel_Worker worker class that is associated */
    protected $worker;

    /** @var string Path to the pipe */
    protected $path;

    /**
     * Initializes the named pipe.
     *
     * @param DocBlox_Parallel_Worker $worker Associated worker.
     */
    public function __construct(DocBlox_Parallel_Worker $worker)
    {
        $this->worker = $worker;

        $this->path = tempnam(sys_get_temp_dir(), 'dpm_');
        posix_mkfifo($this->path, 0750);
    }

    /**
     * If the named pipe was not cleaned up, do so now.
     */
    public function __destruct()
    {
        if (file_exists($this->path)) {
            $this->release();
        }
    }

    /**
     * Pull the worker data into the named pipe.
     *
     * @return void
     */
    public function pull()
    {
        $this->writePipeContents();
    }

    /**
     * Push the worker data back onto the worker and release the pipe.
     *
     * @return void
     */
    public function push()
    {
        list($result, $error, $return_code) = $this->readPipeContents();
        $this->release();

        $this->worker->setResult($result);
        $this->worker->setError($error);
        $this->worker->setReturnCode($return_code);
    }

    /**
     * Convenience method to show relation to readPipeContents.
     *
     * @return void
     */
    protected function writePipeContents()
    {
        // push the gathered data onto a name pipe
        $pipe = fopen($this->path, 'w');
        fwrite(
            $pipe,
            serialize(
                array(
                    $this->worker->getResult(),
                    $this->worker->getError(),
                    $this->worker->getReturnCode()
                )
            )
        );
        fclose($pipe);
    }

    /**
     * Returns the unserialized contents of the pipe.
     *
     * @return array
     */
    protected function readPipeContents()
    {
        $pipe = @fopen($this->path, 'r+');

        if (! $pipe) {
            return array(
                '',
                'Worker died unexpectedly',
                255
            );
        }

        $result = unserialize(fread($pipe, filesize($this->path)));
        fclose($pipe);

        return $result;
    }

    /**
     * Releases the pipe.
     *
     * @return void
     */
    protected function release()
    {
        @unlink($this->path);
    }
}
<?php

/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/Phing.php';
include_once 'phing/Project.php';
include_once 'phing/util/StringHelper.php';
include_once 'phing/system/io/PhingFile.php';
include_once 'phing/system/io/PrintStream.php';

/**
 * A little diagnostic helper that output some information that may help
 * in support. It should quickly give correct information about the
 * phing system.
 */
class Diagnostics
{
    /** utility class */
    private function __construct()
    {
        // hidden constructor
    }

    /**
     * return the list of files existing in PHING_HOME/vendor
     *
     * @param string $type
     *
     * @return array the list of jar files existing in ant.home/lib or
     *               <tt>null</tt> if an error occurs.
     */
    public static function listLibraries($type)
    {
        $home = Phing::getProperty(Phing::PHING_HOME);
        if ($home == null) {
            return null;
        }
        $currentWorkingDir = getcwd();
        chdir($home);
        exec('composer show --' . $type, $packages, $code);
        chdir($currentWorkingDir);

        return $packages;
    }

    /**
     * Print a report to the given stream.
     *
     * @param PrintStream $out the stream to print the report to.
     */
    public static function doReport(PrintStream $out)
    {
        $out->println(str_pad('Phing diagnostics report', 79, "-", STR_PAD_BOTH));
        self::header($out, "Version");
        $out->println(Phing::getPhingVersion());

        self::header($out, "Project properties");
        self::doReportProjectProperties($out);

        self::header($out, "System properties");
        self::doReportSystemProperties($out);

        self::header($out, "PHING_HOME/vendor package listing");
        self::doReportPhingVendorLibraries($out);

        self::header($out, "COMPOSER_HOME/vendor package listing");
        self::doReportComposerSystemLibraries($out);

        self::header($out, "Tasks availability");
        self::doReportTasksAvailability($out);

        self::header($out, "Temp dir");
        self::doReportTempDir($out);
    }

    private static function header(PrintStream $out, $section)
    {
        $out->println(str_repeat('-', 79));
        $out->prints(" ");
        $out->println($section);
        $out->println(str_repeat('-', 79));
    }

    /**
     * Report a listing of system properties existing in the current phing.
     *
     * @param PrintStream $out the stream to print the properties to.
     */
    private static function doReportSystemProperties(PrintStream $out)
    {
        $phing = new Phing();

        $phingprops = $phing->getProperties();

        foreach ($phingprops as $key => $value) {
            $out->println($key . " : " . $value);
        }
    }

    /**
     * Report a listing of project properties.
     *
     * @param PrintStream $out the stream to print the properties to.
     */
    private static function doReportProjectProperties(PrintStream $out)
    {
        $project = new Project();
        $project->init();

        $sysprops = $project->getProperties();

        foreach ($sysprops as $key => $value) {
            $out->println($key . " : " . $value);
        }
    }

    /**
     * Report the content of PHING_HOME/vendor directory
     *
     * @param PrintStream $out the stream to print the content to
     */
    private static function doReportPhingVendorLibraries(PrintStream $out)
    {
        $libs = self::listLibraries('installed');
        self::printLibraries($libs, $out);
    }

    /**
     * Report the content of the global composer library directory
     *
     * @param PrintStream $out the stream to print the content to
     */
    private static function doReportComposerSystemLibraries(PrintStream $out)
    {
        $libs = self::listLibraries('platform');
        self::printLibraries($libs, $out);
    }

    /**
     * list the libraries
     *
     * @param array $libs array of libraries (can be null)
     * @param PrintStream $out output stream
     */
    private static function printLibraries($libs, PrintStream $out)
    {
        if ($libs == null) {
            $out->println("No such directory.");
            return;
        }

        foreach ($libs as $lib) {
            $out->println($lib);
        }
    }

    /**
     * Create a report about all available task in phing.
     *
     * @param PrintStream $out the stream to print the tasks report to
     *                         <tt>null</tt> for a missing stream (ie mapping).
     */
    private static function doReportTasksAvailability(PrintStream $out)
    {
        $project = new Project();
        $project->init();
        $tasks = $project->getTaskDefinitions();
        ksort($tasks);
        foreach ($tasks as $shortName => $task) {
            $out->println($shortName);
        }
    }

    /**
     * try and create a temp file in our temp dir; this
     * checks that it has space and access.
     * We also do some clock reporting.
     *
     * @param PrintStream $out
     */
    private static function doReportTempDir(PrintStream $out)
    {
        $tempdir = PhingFile::getTempDir();
        if ($tempdir == null) {
            $out->println("Warning: php.tmpdir is undefined");
            return;
        }
        $out->println("Temp dir is " . $tempdir);
        $tempDirectory = new PhingFile($tempdir);

        if (!$tempDirectory->exists()) {
            $out->println("Warning, php.tmpdir directory does not exist: " . $tempdir);
            return;

        }

        $now = time();
        $tempFile = PhingFile::createTempFile('diag', 'txt', $tempDirectory);
        $fileWriter = new FileWriter($tempFile);
        $fileWriter->write('some test text');
        $fileWriter->close();

        $filetime = $tempFile->lastModified();

        $tempFile->delete();

        $out->println("Temp dir is writeable");
        $drift = $filetime - $now;
        $out->println("Temp dir alignment with system clock is " . $drift . " s");
        if (abs($drift) > 10) {
            $out->println("Warning: big clock drift -maybe a network filesystem");
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * BuildException + exit status.
 *
 * @author   Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package  phing
 */
class ExitStatusException extends BuildException
{
    /** Status code */
    protected $code;

    /**
     * Constructs an <code>ExitStatusException</code>.
     * @param null|int|string $arg1
     * @param int $arg2
     * @param Location $arg3
     */
    public function __construct($arg1 = null, $arg2 = 0, Location $arg3 = null)
    {
        $methodArgsNum = func_num_args();
        if ($methodArgsNum === 1) {
            parent::__construct();
            $this->code = (int) $arg1;
        } elseif ($methodArgsNum === 2 && is_string($arg1) && is_int($arg2)) {
            parent::__construct($arg1);
            $this->code = $arg2;
        } elseif ($methodArgsNum === 3 && is_string($arg1) && is_int($arg2)) {
            parent::__construct($arg1, $arg3);
            $this->code = $arg2;
        }
    }
}
<?php

/*
 *  $Id: 85cbd982d211b5d05761bd3043d2583b890bdc64 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/system/io/FilterReader.php';
include_once 'phing/system/io/StringReader.php';

/**
 * Base class for core filter readers.
 *
 * @author    <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a>
 * @version   $Id: 85cbd982d211b5d05761bd3043d2583b890bdc64 $
 * @see       FilterReader
 * @package   phing.filters
 */
class BaseFilterReader extends FilterReader
{

    /** Have the parameters passed been interpreted? */
    protected $initialized = false;

    /** The Phing project this filter is part of. */
    protected $project = null;

    /**
     * Constructor used by Phing's introspection mechanism.
     * The original filter reader is only used for chaining
     * purposes, never for filtering purposes (and indeed
     * it would be useless for filtering purposes, as it has
     * no real data to filter). ChainedReaderHelper uses
     * this placeholder instance to create a chain of real filters.
     *
     * @param Reader $in
     */
    public function __construct($in = null)
    {
        if ($in === null) {
            $dummy = "";
            $in = new StringReader($dummy);
        }
        parent::__construct($in);
    }

    /**
     * Returns the initialized status.
     *
     * @return boolean whether or not the filter is initialized
     */
    public function getInitialized()
    {
        return $this->initialized;
    }

    /**
     * Sets the initialized status.
     *
     * @param boolean $initialized Whether or not the filter is initialized.
     */
    public function setInitialized($initialized)
    {
        $this->initialized = (boolean) $initialized;
    }

    /**
     * Sets the project to work with.
     *
     * @param object|Project $project The project this filter is part of.
     *                        Should not be <code>null</code>.
     */
    public function setProject(Project $project)
    {
        // type check, error must never occur, bad code of it does
        $this->project = $project;
    }

    /**
     * Returns the project this filter is part of.
     *
     * @return object The project this filter is part of
     */
    public function getProject()
    {
        return $this->project;
    }

    /**
     * Reads characters.
     *
     * @param  int $len  Maximum number of characters to read.
     *
     * @return string Characters read, or -1 if the end of the stream
     *                    has been reached
     *
     * @throws IOException If an I/O error occurs
     */
    public function read($len = null)
    {
        return $this->in->read($len);
    }

    /**
     * Reads a line of text ending with '\n' (or until the end of the stream).
     * The returned String retains the '\n'.
     *
     * @return string the line read, or <code>null</code> if the end of the
     *             stream has already been reached
     *
     * @throws IOException if the underlying reader throws one during
     *                     reading
     */
    public function readLine()
    {
        $line = null;

        while (($ch = $this->in->read(1)) !== -1) {
            $line .= $ch;
            if ($ch === "\n") {
                break;
            }
        }

        return $line;
    }

    /**
     * Returns whether the end of file has been reached with input stream.
     *
     * @return boolean
     */
    public function eof()
    {
        return $this->in->eof();
    }

    /**
     * Convenience method to support logging in filters.
     *
     * @param string $msg   Message to log.
     * @param int    $level Priority level.
     *
     * @return void
     */
    public function log($msg, $level = Project::MSG_INFO)
    {
        if ($this->project !== null) {
            $this->project->log("[filter:" . get_class($this) . "] " . $msg, $level);
        }
    }
}
<?php
/**
 * TTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/filters/BaseFilterReader.php';
include_once 'phing/types/Parameterizable.php';
include_once 'phing/types/Parameter.php';

/**
 * Base class for core filter readers.
 *
 * @author    Yannick Lecaillez <yl@seasonfive.com>
 * @copyright 2003 seasonfive. All rights reserved
 *
 * @see       FilterReader
 * @package   phing.filters
 */
class BaseParamFilterReader extends BaseFilterReader implements Parameterizable
{
    /**
     * The passed in parameter array.
     *
     * @var array $_parameters
     */
    protected $_parameters = array();

    /**
     * Sets the parameters used by this filter, and sets
     * the filter to an uninitialized status.
     *
     * @param array $parameters Array of parameters to be used by this filter.
     *              Should not be <code>null</code>.
     *
     * @return void
     *
     * @throws Exception
     */
    public function setParameters($parameters)
    {
        // type check, error must never occur, bad code of it does
        if (!is_array($parameters)) {
            throw new Exception("Expected parameters array got something else");
        }

        $this->_parameters = $parameters;
        $this->setInitialized(false);
    }

    /**
     * Returns the parameters to be used by this filter.
     *
     * @return array the parameters to be used by this filter
     */
    public function &getParameters()
    {
        return $this->_parameters;
    }
}
<?php

/*
 *  $Id: 372e133ade942dbf87ea7e148b86116459dd4050 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

/**
 * Interface indicating that a reader may be chained to another one.
 *
 * @author Magesh Umasankar
 * @package phing.filters
 */
interface ChainableReader
{

    /**
     * Returns a reader with the same configuration as this one,
     * but filtering input from the specified reader.
     *
     * @param Reader $rdr the reader which the returned reader should be filtering
     *
     * @return Reader A reader with the same configuration as this one, but
     *                filtering input from the specified reader
     */
    public function chain(Reader $rdr);
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/filters/BaseParamFilterReader.php';
include_once 'phing/filters/ChainableReader.php';
include_once 'phing/system/io/PhingFile.php';
include_once 'phing/system/io/BufferedReader.php';
include_once 'phing/system/io/FileReader.php';

/**
 * Concats a file before and/or after the file.
 *
 * Example:
 * ```
 * <copy todir="build">
 *     <fileset dir="src" includes="*.php"/>
 *     <filterchain>
 *         <concatfilter prepend="license.txt"/>
 *     </filterchain>
 * </copy>
 * ```
 *
 * Copies all php sources from `src` to `build` and adds the
 * content of `license.txt` add the beginning of each
 * file.
 *
 * @author  Siad.ardroumli <siad.ardroumli@gmail.com>
 * @package phing.filters
 */
class ConcatFilter extends BaseParamFilterReader implements ChainableReader
{
    /**
     * File to add before the content.
     *
     * @var PhingFile $prepend
     */
    private $prepend;

    /**
     * File to add after the content.
     *
     * @var PhingFile $append
     */
    private $append;

    /**
     * Reader for prepend-file.
     * @var BufferedReader
     */
    private $prependReader;

    /**
     * Reader for append-file.
     * @var BufferedReader
     */
    private $appendReader;

    /**
     * @param Reader $in
     */
    public function __construct(Reader $in = null)
    {
        parent::__construct($in);
    }

    /**
     * Returns the next character in the filtered stream. If the desired
     * number of lines have already been read, the resulting stream is
     * effectively at an end. Otherwise, the next character from the
     * underlying stream is read and returned.
     *
     * @param int $len
     * @return int|string the next character in the resulting stream, or -1
     * if the end of the resulting stream has been reached
     *
     * @throws IOException if the underlying stream throws an IOException
     *                     during reading
     * @throws BuildException
     */
    public function read($len = 0)
    {
        // do the "singleton" initialization
        if (!$this->getInitialized()) {
            $this->initialize();
            $this->setInitialized(true);
        }

        $ch = -1;

        // The readers return -1 if they end. So simply read the "prepend"
        // after that the "content" and at the end the "append" file.
        if ($this->prependReader !== null) {
            $ch = $this->prependReader->read();
            if ($ch === -1) {
                // I am the only one so I have to close the reader
                $this->prependReader->close();
                $this->prependReader = null;
            }
        }
        if ($ch === -1) {
            $ch = parent::read();
        }
        if ($ch === -1 && $this->appendReader !== null) {
            $ch = $this->appendReader->read();
            if ($ch === -1) {
                // I am the only one so I have to close the reader
                $this->appendReader->close();
                $this->appendReader = null;
            }
        }

        return $ch;
    }

    /**
     * Scans the parameters list for the "lines" parameter and uses
     * it to set the number of lines to be returned in the filtered stream.
     * also scan for skip parameter.
     *
     * @throws BuildException
     */
    private function initialize()
    {
        // get parameters
        $params = $this->getParameters();
        if ($params !== null) {
            /** @var Parameter $param */
            foreach ($params as $param) {
                if ('prepend' === $param->getName()) {
                    $this->setPrepend(new PhingFile($param->getValue()));
                    continue;
                }
                if ('append' === $param->getName()) {
                    $this->setAppend(new PhingFile($param->getValue()));
                    continue;
                }
            }
        }
        if ($this->prepend !== null) {
            if (!$this->prepend->isAbsolute()) {
                $this->prepend = new PhingFile($this->getProject()->getBasedir(), $this->prepend->getPath());
            }
            $this->prependReader = new BufferedReader(new FileReader($this->prepend));
        }
        if ($this->append !== null) {
            if (!$this->append->isAbsolute()) {
                $this->append = new PhingFile($this->getProject()->getBasedir(), $this->append->getPath());
            }
            $this->appendReader = new BufferedReader(new FileReader($this->append));
        }
    }

    /**
     * Creates a new ConcatReader using the passed in
     * Reader for instantiation.
     *
     * @param Reader $rdr A Reader object providing the underlying stream.
     *                    Must not be <code>null</code>.
     *
     * @return ConcatFilter a new filter based on this configuration, but filtering
     *                      the specified reader
     */
    public function chain(Reader $rdr)
    {
        $newFilter = new ConcatFilter($rdr);
        $newFilter->setProject($this->getProject());
        $newFilter->setPrepend($this->getPrepend());
        $newFilter->setAppend($this->getAppend());

        return $newFilter;
    }

    /**
     * Returns `prepend` attribute.
     * @return PhingFile prepend attribute
     */
    public function getPrepend()
    {
        return $this->prepend;
    }

    /**
     * Sets `prepend` attribute.
     * @param PhingFile|string prepend new value
     */
    public function setPrepend($prepend)
    {
        if ($prepend instanceof PhingFile) {
            $this->prepend = $prepend;
        } else {
            $this->prepend = new PhingFile($prepend);
        }
    }

    /**
     * Returns `append` attribute.
     * @return PhingFile append attribute
     */
    public function getAppend()
    {
        return $this->append;
    }

    /**
     * Sets `append` attribute.
     * @param PhingFile|string append new value
     */
    public function setAppend($append)
    {
        $this->append = $append;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/filters/BaseFilterReader.php';
require_once 'phing/filters/ChainableReader.php';

/**
 * UTF-8 to Unicode Code Points
 *
 * This method converts non-latin characters to unicode escapes.
 * Useful to load properties containing non latin.
 *
 * Example:
 *
 * `<escapeunicode>`
 *
 * Or:
 *
 * `<filterreader classname="phing.filters.EscapeUnicode"/>`
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing.filters
 */
class EscapeUnicode extends BaseFilterReader implements ChainableReader
{
    /**
     * Returns the next line in the filtered stream, converting non latin
     * characters to unicode escapes.
     *
     * @param int $len     optional
     * @return string      the converted lines in the resulting stream, or -1
     *                     if the end of the resulting stream has been reached
     * @throws IOException if the underlying stream throws
     *                     an IOException during reading
     */
    public function read($len = null)
    {
        if (!$this->getInitialized()) {
            $this->initialize();
            $this->setInitialized(true);
        }

        // Process whole text at once.
        $text = null;
        while (($data = $this->in->read($len)) !== -1) {
            $text .= $data;
        }

        // At the end.
        if (null === $text) {
            return -1;
        }

        $textArray = preg_split("~\R~", $text);

        $lines = array();
        foreach ($textArray as $offset => $line) {
            $lines[] = trim(json_encode($line), '"');
            if (strlen($line) !== strlen($lines[$offset])) {
                $this->log(
                    "Escape unicode chars on line " . ($offset + 1)
                    . " from " . $line . " to " . $lines[$offset],
                    Project::MSG_VERBOSE
                );
            }
        }

        $escaped = implode(PHP_EOL, $lines);

        return $escaped;
    }

    /**
     * Creates a new EscapeUnicode using the passed in
     * Reader for instantiation.
     *
     * @param rdr A Reader object providing the underlying stream.
     *            Must not be <code>null</code>.
     *
     * @return a new filter based on this configuration, but filtering
     *         the specified reader
     */
    public function chain(Reader $rdr)
    {
        $newFilter = new EscapeUnicode($rdr);
        $newFilter->setInitialized(true);
        $newFilter->setProject($this->getProject());

        return $newFilter;
    }

    /**
     * Parses the parameters (currently unused)
     */
    private function initialize()
    {
    }
}
<?php

/*
 *  $Id: e675c5b0a48221a1627266abd6e88c1d495badbc $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

require_once 'phing/filters/BaseFilterReader.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * Expands Phing Properties, if any, in the data.
 * <p>
 * Example:<br>
 * <pre><expandproperties/></pre>
 * Or:
 * <pre><filterreader classname="phing.filters.ExpandProperties'/></pre>
 *
 * @author    Yannick Lecaillez <yl@seasonfive.com>
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: e675c5b0a48221a1627266abd6e88c1d495badbc $
 * @see       BaseFilterReader
 * @package   phing.filters
 */
class ExpandProperties extends BaseFilterReader implements ChainableReader
{
    protected $logLevel = Project::MSG_VERBOSE;

    /**
     * Set level of log messages generated (default = info)
     * @param string $level
     */
    public function setLevel($level)
    {
        switch ($level) {
            case "error":
                $this->logLevel = Project::MSG_ERR;
                break;
            case "warning":
                $this->logLevel = Project::MSG_WARN;
                break;
            case "info":
                $this->logLevel = Project::MSG_INFO;
                break;
            case "verbose":
                $this->logLevel = Project::MSG_VERBOSE;
                break;
            case "debug":
                $this->logLevel = Project::MSG_DEBUG;
                break;
        }
    }

    /**
     * Returns the filtered stream.
     * The original stream is first read in fully, and the Phing properties are expanded.
     *
     * @param null $len
     * @return mixed the filtered stream, or -1 if the end of the resulting stream has been reached.
     *
     * @exception IOException if the underlying stream throws an IOException
     * during reading
     */
    public function read($len = null)
    {

        $buffer = $this->in->read($len);

        if ($buffer === -1) {
            return -1;
        }

        $project = $this->getProject();
        $buffer = ProjectConfigurator::replaceProperties($project, $buffer, $project->getProperties(), $this->logLevel);

        return $buffer;
    }

    /**
     * Creates a new ExpandProperties filter using the passed in
     * Reader for instantiation.
     *
     * @param Reader $reader A Reader object providing the underlying stream.
     *               Must not be <code>null</code>.
     *
     * @return ExpandProperties A new filter based on this configuration, but filtering
     *                the specified reader
     */
    public function chain(Reader $reader)
    {
        $newFilter = new ExpandProperties($reader);
        $newFilter->setProject($this->getProject());

        return $newFilter;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/filters/BaseParamFilterReader.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * Reads the first <code>n</code> lines of a stream.
 * (Default is first 10 lines.)
 * <p>
 * Example:
 * <pre><headfilter lines="3"/></pre>
 * Or:
 * <pre><filterreader classname="phing.filters.HeadFilter">
 *    <param name="lines" value="3"/>
 * </filterreader></pre>
 *
 * @author    <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a>
 * @author    hans lellelid, hans@velum.net
 *
 * @see       FilterReader
 *
 * @package   phing.filters
 */
class HeadFilter extends BaseParamFilterReader implements ChainableReader
{
    /**
     * Parameter name for the number of lines to be returned.
     */
    const LINES_KEY = "lines";

    /**
     * Number of lines currently read in.
     *
     * @var integer
     */
    private $_linesRead = 0;

    /**
     * Number of lines to be returned in the filtered stream.
     *
     * @var integer
     */
    private $_lines = 10;

    /**
     * Returns first n lines of stream.
     *
     * @param null $len
     * @return string|int the resulting stream, or -1
     *                    if the end of the resulting stream has been reached
     *
     */
    public function read($len = null)
    {

        if (!$this->getInitialized()) {
            $this->_initialize();
            $this->setInitialized(true);
        }

        // note, if buffer contains fewer lines than
        // $this->_lines this code will not work.

        if ($this->_linesRead < $this->_lines) {

            $buffer = $this->in->read($len);

            if ($buffer === -1) {
                return -1;
            }

            // now grab first X lines from buffer

            $lines = explode("\n", $buffer);

            $linesCount = count($lines);

            // must account for possibility that the num lines requested could
            // involve more than one buffer read.
            $len = ($linesCount > $this->_lines ? $this->_lines - $this->_linesRead : $linesCount);
            $filtered_buffer = implode("\n", array_slice($lines, 0, $len));
            $this->_linesRead += $len;

            return $filtered_buffer;

        }

        return -1; // EOF, since the file is "finished" as far as subsequent filters are concerned.
    }

    /**
     * Sets the number of lines to be returned in the filtered stream.
     *
     * @param integer $lines the number of lines to be returned in the filtered stream.
     *
     * @return void
     */
    public function setLines($lines)
    {
        $this->_lines = (int) $lines;
    }

    /**
     * Returns the number of lines to be returned in the filtered stream.
     *
     * @return integer The number of lines to be returned in the filtered stream.
     */
    public function getLines()
    {
        return $this->_lines;
    }

    /**
     * Creates a new HeadFilter using the passed in
     * Reader for instantiation.
     *
     * @param Reader $reader A Reader object providing the underlying stream.
     *                       Must not be <code>null</code>.
     *
     * @return HeadFilter A new filter based on this configuration, but filtering
     *                    the specified reader.
     */
    public function chain(Reader $reader)
    {
        $newFilter = new HeadFilter($reader);
        $newFilter->setLines($this->getLines());
        $newFilter->setInitialized(true);
        $newFilter->setProject($this->getProject());

        return $newFilter;
    }

    /**
     * Scans the parameters list for the "lines" parameter and uses
     * it to set the number of lines to be returned in the filtered stream.
     *
     * @return void
     */
    private function _initialize()
    {
        $params = $this->getParameters();
        if ($params !== null) {
            for ($i = 0, $_i = count($params); $i < $_i; $i++) {
                if (self::LINES_KEY == $params[$i]->getName()) {
                    $this->_lines = (int) $params[$i]->getValue();
                    break;
                }
            }
        }
    }
}
<?php

/*
 *  $Id: fdd2765ea9675f36fde778b3faf373505cc0ccc2 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/filters/BaseParamFilterReader.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * Encode data from <code>in</code> encoding to <code>out</code> encoding.
 *
 * Example:
 * <pre>
 * <iconvfilter inputencoding="UTF-8" outputencoding="CP1251" />
 * </pre>
 * Or:
 * <pre>
 * <filterreader classname="phing.filters.IconvFilter">
 *    <param name="inputencoding" value="UTF-8" />
 *    <param name="outputencoding" value="CP1251" />
 * </filterreader>
 * </pre>
 *
 * @author    Alexey Shockov, <alexey@shockov.com>
 * @version   $Id: fdd2765ea9675f36fde778b3faf373505cc0ccc2 $
 * @package   phing.filters
 */
class IconvFilter
    extends BaseParamFilterReader
    implements ChainableReader
{

    private $_inputEncoding;

    private $_outputEncoding;

    /**
     * Returns first n lines of stream.
     * @param null $len
     * @return the resulting stream, or -1
     *             if the end of the resulting stream has been reached
     *
     * @exception IOException if the underlying stream throws an IOException
     * during reading
     */
    public function read($len = null)
    {
        $this->_initialize();

        // Process whole text at once.
        $text = null;
        while (($data = $this->in->read($len)) !== -1) {
            $text .= $data;
        }

        // At the end.
        if (null === $text) {
            return -1;
        }

        $this->log(
            "Encoding " . $this->in->getResource() . " from " . $this->getInputEncoding(
            ) . " to " . $this->getOutputEncoding(),
            Project::MSG_VERBOSE
        );

        return iconv($this->_inputEncoding, $this->_outputEncoding, $text);
    }

    /**
     *
     * @param string $encoding Input encoding.
     */
    public function setInputEncoding($encoding)
    {
        $this->_inputEncoding = $encoding;
    }

    /**
     *
     * @return string
     */
    public function getInputEncoding()
    {
        return $this->_inputEncoding;
    }

    /**
     *
     * @param string $encoding Output encoding.
     */
    public function setOutputEncoding($encoding)
    {
        $this->_outputEncoding = $encoding;
    }

    /**
     *
     * @return string
     */
    public function getOutputEncoding()
    {
        return $this->_outputEncoding;
    }

    /**
     * Creates a new IconvFilter using the passed in Reader for instantiation.
     *
     * @param Reader $reader
     * @internal param A $object Reader object providing the underlying stream. Must not be <code>null</code>.
     *
     * @return object A new filter based on this configuration, but filtering the specified reader.
     */
    public function chain(Reader $reader)
    {
        $filter = new self($reader);

        $filter->setInputEncoding($this->getInputEncoding());
        $filter->setOutputEncoding($this->getOutputEncoding());

        $filter->setInitialized(true);
        $filter->setProject($this->getProject());

        return $filter;
    }

    /**
     * Configuring object from the parameters list.
     */
    private function _initialize()
    {
        if ($this->getInitialized()) {
            return;
        }

        $params = $this->getParameters();
        if ($params !== null) {
            foreach ($params as $param) {
                if ('in' == $param->getName()) {
                    $this->setInputEncoding($param->getValue());
                } else {
                    if ('out' == $param->getName()) {
                        $this->setOutputEncoding($param->getValue());
                    }
                }
            }
        }

        $this->setInitialized(true);
    }
}
<?php

/*
 *  $Id: feaa90aaa736282a3599e8e5f655c31870b436cc $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/filters/BaseParamFilterReader.php';
include_once 'phing/filters/BaseFilterReader.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * Filter which includes only those lines that contain all the user-specified
 * strings.
 *
 * Example:
 *
 * <pre><linecontains>
 *   <contains value="foo">
 *   <contains value="bar">
 * </linecontains></pre>
 *
 * Or:
 *
 * <pre><filterreader classname="phing.filters.LineContains">
 *    <param type="contains" value="foo"/>
 *    <param type="contains" value="bar"/>
 * </filterreader></pre>
 *
 * This will include only those lines that contain <code>foo</code> and
 * <code>bar</code>.
 *
 * @author    Yannick Lecaillez <yl@seasonfive.com>
 * @author    Hans Lellelid <hans@velum.net>
 * @version   $Id: feaa90aaa736282a3599e8e5f655c31870b436cc $
 * @see       PhingFilterReader
 * @package   phing.filters
 */
class LineContains extends BaseParamFilterReader implements ChainableReader
{

    /**
     * The parameter name for the string to match on.
     * @var string
     */
    const CONTAINS_KEY = "contains";

    /**
     * Array of Contains objects.
     * @var array
     */
    private $_contains = array();

    /**
     * [Deprecated]
     * @var string
     */
    private $_line = null;

    /**
     * Returns all lines in a buffer that contain specified strings.
     * @param null $len
     * @return mixed buffer, -1 on EOF
     */
    public function read($len = null)
    {
        if (!$this->getInitialized()) {
            $this->_initialize();
            $this->setInitialized(true);
        }

        $buffer = $this->in->read($len);

        if ($buffer === -1) {
            return -1;
        }

        $lines = explode("\n", $buffer);
        $matched = array();
        $containsSize = count($this->_contains);

        foreach ($lines as $line) {
            for ($i = 0; $i < $containsSize; $i++) {
                $containsStr = $this->_contains[$i]->getValue();
                if (strstr($line, $containsStr) === false) {
                    $line = null;
                    break;
                }
            }
            if ($line !== null) {
                $matched[] = $line;
            }
        }
        $filtered_buffer = implode("\n", $matched);

        return $filtered_buffer;
    }

    /**
     * [Deprecated. For reference only, used to be read() method.]
     * Returns the next character in the filtered stream, only including
     * lines from the original stream which contain all of the specified words.
     *
     * @return the next character in the resulting stream, or -1
     *             if the end of the resulting stream has been reached
     *
     * @exception IOException if the underlying stream throws an IOException
     * during reading
     */
    public function readChar()
    {
        if (!$this->getInitialized()) {
            $this->_initialize();
            $this->setInitialized(true);
        }

        $ch = -1;

        if ($this->_line !== null) {
            $ch = substr($this->_line, 0, 1);
            if (strlen($this->_line) === 1) {
                $this->_line = null;
            } else {
                $this->_line = substr($this->_line, 1);
            }
        } else {
            $this->_line = $this->readLine();
            if ($this->_line === null) {
                $ch = -1;
            } else {
                $containsSize = count($this->_contains);
                for ($i = 0; $i < $containsSize; $i++) {
                    $containsStr = $this->_contains[$i]->getValue();
                    if (strstr($this->_line, $containsStr) === false) {
                        $this->_line = null;
                        break;
                    }
                }

                return $this->readChar();
            }
        }

        return $ch;
    }

    /**
     * Adds a <code>contains</code> nested element.
     *
     * @return Contains The <code>contains</code> element added.
     *                  Must not be <code>null</code>.
     */
    public function createContains()
    {
        $num = array_push($this->_contains, new Contains());

        return $this->_contains[$num - 1];
    }

    /**
     * Sets the array of words which must be contained within a line read
     * from the original stream in order for it to match this filter.
     *
     * @param array $contains An array of words which must be contained
     *                        within a line in order for it to match in this filter.
     *                        Must not be <code>null</code>.
     * @throws Exception
     */
    public function setContains($contains)
    {
        // type check, error must never occur, bad code of it does
        if (!is_array($contains)) {
            throw new Exception("Excpected array got something else");
        }

        $this->_contains = $contains;
    }

    /**
     * Returns the vector of words which must be contained within a line read
     * from the original stream in order for it to match this filter.
     *
     * @return array The array of words which must be contained within a line read
     *               from the original stream in order for it to match this filter. The
     *               returned object is "live" - in other words, changes made to the
     *               returned object are mirrored in the filter.
     */
    public function getContains()
    {
        return $this->_contains;
    }

    /**
     * Creates a new LineContains using the passed in
     * Reader for instantiation.
     *
     * @param Reader $reader A Reader object providing the underlying stream.
     *                       Must not be <code>null</code>.
     *
     * @return LineContains A new filter based on this configuration, but filtering
     *                      the specified reader
     */
    public function chain(Reader $reader)
    {
        $newFilter = new LineContains($reader);
        $newFilter->setContains($this->getContains());
        $newFilter->setInitialized(true);
        $newFilter->setProject($this->getProject());

        return $newFilter;
    }

    /**
     * Parses the parameters to add user-defined contains strings.
     */
    private function _initialize()
    {
        $params = $this->getParameters();
        if ($params !== null) {
            foreach ($params as $param) {
                if (self::CONTAINS_KEY == $param->getType()) {
                    $cont = new Contains();
                    $cont->setValue($param->getValue());
                    array_push($this->_contains, $cont);
                    break; // because we only support a single contains
                }
            }
        }
    }
}

/**
 * Holds a contains element.
 *
 * @package phing.filters
 */
class Contains
{

    /**
     * @var string
     */
    private $_value;

    /**
     * Set 'contains' value.
     * @param string $contains
     */
    public function setValue($contains)
    {
        $this->_value = (string) $contains;
    }

    /**
     * Returns 'contains' value.
     * @return string
     */
    public function getValue()
    {
        return $this->_value;
    }
}
<?php
/*
 *  $Id: 738a0e1b2434adba882fd21fa59f8a2f41377980 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/filters/BaseParamFilterReader.php';
include_once 'phing/types/RegularExpression.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * Filter which includes only those lines that contain the user-specified
 * regular expression matching strings.
 *
 * Example:
 * <pre><linecontainsregexp>
 *   <regexp pattern="foo*">
 * </linecontainsregexp></pre>
 *
 * Or:
 *
 * <pre><filterreader classname="phing.filters.LineContainsRegExp">
 *    <param type="regexp" value="foo*"/>
 * </filterreader></pre>
 *
 * This will fetch all those lines that contain the pattern <code>foo</code>
 *
 * @author    Yannick Lecaillez <yl@seasonfive.com>
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: 738a0e1b2434adba882fd21fa59f8a2f41377980 $
 * @see       FilterReader
 * @package   phing.filters
 */
class LineContainsRegexp extends BaseParamFilterReader implements ChainableReader
{

    /**
     * Parameter name for regular expression.
     * @var string
     */
    const REGEXP_KEY = "regexp";

    /**
     * Regular expressions that are applied against lines.
     * @var array
     */
    private $_regexps = array();

    /**
     * Returns all lines in a buffer that contain specified strings.
     * @param null $len
     * @return mixed buffer, -1 on EOF
     */
    public function read($len = null)
    {

        if (!$this->getInitialized()) {
            $this->_initialize();
            $this->setInitialized(true);
        }

        $buffer = $this->in->read($len);

        if ($buffer === -1) {
            return -1;
        }

        $lines = explode("\n", $buffer);
        $matched = array();

        $regexpsSize = count($this->_regexps);
        foreach ($lines as $line) {
            for ($i = 0; $i < $regexpsSize; $i++) {
                $regexp = $this->_regexps[$i];
                $re = $regexp->getRegexp($this->getProject());
                $matches = $re->matches($line);
                if (!$matches) {
                    $line = null;
                    break;
                }
            }
            if ($line !== null) {
                $matched[] = $line;
            }
        }
        $filtered_buffer = implode("\n", $matched);

        return $filtered_buffer;
    }

    /**
     * Adds a <code>regexp</code> element.
     *
     * @return object regExp The <code>regexp</code> element added.
     */
    public function createRegexp()
    {
        $num = array_push($this->_regexps, new RegularExpression());

        return $this->_regexps[$num - 1];
    }

    /**
     * Sets the vector of regular expressions which must be contained within
     * a line read from the original stream in order for it to match this
     * filter.
     *
     * @param An $regexps
     * @throws Exception
     * @internal param An $regexps array of regular expressions which must be contained
     *                within a line in order for it to match in this filter. Must not be
     *                <code>null</code>.
     */
    public function setRegexps($regexps)
    {
        // type check, error must never occur, bad code of it does
        if (!is_array($regexps)) {
            throw new Exception("Excpected an 'array', got something else");
        }
        $this->_regexps = $regexps;
    }

    /**
     * Returns the array of regular expressions which must be contained within
     * a line read from the original stream in order for it to match this
     * filter.
     *
     * @return array The array of regular expressions which must be contained within
     *               a line read from the original stream in order for it to match this
     *               filter. The returned object is "live" - in other words, changes made to
     *               the returned object are mirrored in the filter.
     */
    public function getRegexps()
    {
        return $this->_regexps;
    }

    /**
     * Creates a new LineContainsRegExp using the passed in
     * Reader for instantiation.
     *
     * @param Reader $reader
     * @throws Exception
     * @internal param A $object Reader object providing the underlying stream.
     *               Must not be <code>null</code>.
     *
     * @return object A new filter based on this configuration, but filtering
     *                the specified reader
     */
    public function chain(Reader $reader)
    {
        $newFilter = new LineContainsRegExp($reader);
        $newFilter->setRegexps($this->getRegexps());
        $newFilter->setInitialized(true);
        $newFilter->setProject($this->getProject());

        return $newFilter;
    }

    /**
     * Parses parameters to add user defined regular expressions.
     */
    private function _initialize()
    {
        $params = $this->getParameters();
        if ($params !== null) {
            for ($i = 0; $i < count($params); $i++) {
                if (self::REGEXP_KEY === $params[$i]->getType()) {
                    $pattern = $params[$i]->getValue();
                    $regexp = new RegularExpression();
                    $regexp->setPattern($pattern);
                    array_push($this->_regexps, $regexp);
                }
            }
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/BuildException.php';
include_once 'phing/filters/BaseParamFilterReader.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * Applies a native php function to the original input.
 *
 * Example:
 * <pre><phparraymaplines function="strtoupper"/></pre>
 *
 * Or:
 *
 * <pre><filterreader classname="phing.filters.PhpArrayMapLines">
 *  <param name="function" value="strtoupper"/>
 * </filterreader></pre>
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing.filters
 */
class PhpArrayMapLines extends BaseParamFilterReader implements ChainableReader
{
    /**
     * Parameter name for the function.
     * @var string
     */
    const FUNCTION_KEY = "function";

    /**
     * The function to be used.
     * @var string
     */
    private $function = null;

    /**
     * Applies a native php function to the original input and returns resulting stream.
     *
     * @param null $len
     * @return mixed buffer, -1 on EOF
     */
    public function read($len = null)
    {
        if (!$this->getInitialized()) {
            $this->_initialize();
            $this->checkAttributes();
            $this->setInitialized(true);
        }

        $buffer = $this->in->read($len);

        if ($buffer === -1 || !function_exists($this->function)) {
            return -1;
        }

        $lines = explode("\n", $buffer);

        $filtered = array_map($this->function, $lines);

        $filtered_buffer = implode("\n", $filtered);

        return $filtered_buffer;
    }

    /**
     * Sets the function used by array_map.
     *
     * @param string $function The function used by array_map.
     */
    public function setFunction($function)
    {
        $this->function = (string) $function;
    }

    /**
     * Returns the prefix which will be added at the start of each input line.
     *
     * @return string The prefix which will be added at the start of each input line
     */
    public function getFunction()
    {
        return $this->function;
    }

    /**
     * Make sure that required attributes are set.
     * @throws BuildException - if any required attribs aren't set.
     */
    protected function checkAttributes()
    {
        if (!$this->function) {
            throw new BuildException("You must specify a value for the 'function' attribute.");
        }
    }

    /**
     * Creates a new PhpArrayMapLines filter using the passed in
     * Reader for instantiation.
     *
     * @param Reader $reader Reader object providing the underlying stream.
     *                       Must not be <code>null</code>.
     *
     * @return PhpArrayMapLines A new filter based on this configuration, but filtering
     *                          the specified reader
     */
    public function chain(Reader $reader)
    {
        $newFilter = new PhpArrayMapLines($reader);
        $newFilter->setFunction($this->getFunction());
        $newFilter->setInitialized(true);
        $newFilter->setProject($this->getProject());

        return $newFilter;
    }

    /**
     * Initializes the function if it is available from the parameters.
     */
    private function _initialize()
    {
        $params = $this->getParameters();
        if ($params !== null) {
            for ($i = 0, $_i = count($params); $i < $_i; $i++) {
                if (self::FUNCTION_KEY == $params[$i]->getName()) {
                    $this->function = (string) $params[$i]->getValue();
                    break;
                }
            }
        }
    }
}
<?php

/*
 *  $Id: 71f5cbabef2b2bfd436cd79187e0cecd172d10a2 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/filters/BaseParamFilterReader.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * Attaches a prefix to every line.
 *
 * Example:
 * <pre><prefixlines prefix="Foo"/></pre>
 *
 * Or:
 *
 * <pre><filterreader classname="phing.filters.PrefixLines">
 *  <param name="prefix" value="Foo"/>
 * </filterreader></pre>
 *
 * @author    <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a>
 * @author    hans lellelid, hans@velum.net
 * @version   $Id: 71f5cbabef2b2bfd436cd79187e0cecd172d10a2 $
 * @see       FilterReader
 * @package   phing.filters
 */
class PrefixLines extends BaseParamFilterReader implements ChainableReader
{

    /**
     * Parameter name for the prefix.
     * @var string
     */
    const PREFIX_KEY = "lines";

    /**
     * The prefix to be used.
     * @var string
     */
    private $_prefix = null;

    /**
     * Adds a prefix to each line of input stream and returns resulting stream.
     *
     * @param null $len
     * @return mixed buffer, -1 on EOF
     */
    public function read($len = null)
    {
        if (!$this->getInitialized()) {
            $this->_initialize();
            $this->setInitialized(true);
        }

        $buffer = $this->in->read($len);

        if ($buffer === -1) {
            return -1;
        }

        $lines = explode("\n", $buffer);
        $filtered = array();

        foreach ($lines as $line) {
            $line = $this->_prefix . $line;
            $filtered[] = $line;
        }

        $filtered_buffer = implode("\n", $filtered);

        return $filtered_buffer;
    }

    /**
     * Sets the prefix to add at the start of each input line.
     *
     * @param string $prefix The prefix to add at the start of each input line.
     *                       May be <code>null</code>, in which case no prefix
     *                       is added.
     */
    public function setPrefix($prefix)
    {
        $this->_prefix = (string) $prefix;
    }

    /**
     * Returns the prefix which will be added at the start of each input line.
     *
     * @return string The prefix which will be added at the start of each input line
     */
    public function getPrefix()
    {
        return $this->_prefix;
    }

    /**
     * Creates a new PrefixLines filter using the passed in
     * Reader for instantiation.
     *
     * @param Reader $reader
     * @internal param A $object Reader object providing the underlying stream.
     *               Must not be <code>null</code>.
     *
     * @return object A new filter based on this configuration, but filtering
     *                the specified reader
     */
    public function chain(Reader $reader)
    {
        $newFilter = new PrefixLines($reader);
        $newFilter->setPrefix($this->getPrefix());
        $newFilter->setInitialized(true);
        $newFilter->setProject($this->getProject());

        return $newFilter;
    }

    /**
     * Initializes the prefix if it is available from the parameters.
     */
    private function _initialize()
    {
        $params = $this->getParameters();
        if ($params !== null) {
            for ($i = 0, $_i = count($params); $i < $_i; $i++) {
                if (self::PREFIX_KEY == $params[$i]->getName()) {
                    $this->_prefix = (string) $params[$i]->getValue();
                    break;
                }
            }
        }
    }
}
<?php

/*
 *  $Id: 4f9916cf83e322464568d6b0d4946f131ecf1c32 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

require_once 'phing/filters/BaseFilterReader.php';
include_once 'phing/filters/ChainableReader.php';
include_once 'phing/types/RegularExpression.php';

/**
 * Performs a regexp find/replace on stream.
 * <p>
 * Example:<br>
 * <pre>
 * <replaceregexp>
 *    <regexp pattern="\r\n" replace="\n"/>
 *    <regexp pattern="(\w+)\.xml" replace="\1.php" ignoreCase="true"/>
 * </replaceregexp>
 * </pre>
 *
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: 4f9916cf83e322464568d6b0d4946f131ecf1c32 $
 * @package   phing.filters
 */
class ReplaceRegexp extends BaseFilterReader implements ChainableReader
{

    /**
     * @var array RegularExpression[]
     */
    private $regexps = array();

    /**
     * Creator method handles nested <regexp> tags.
     * @return RegularExpression
     */
    public function createRegexp()
    {
        $num = array_push($this->regexps, new RegularExpression());

        return $this->regexps[$num - 1];
    }

    /**
     * Sets the current regexps.
     * (Used when, e.g., cloning/chaining the method.)
     * @param array RegularExpression[]
     */
    public function setRegexps($regexps)
    {
        $this->regexps = $regexps;
    }

    /**
     * Gets the current regexps.
     * (Used when, e.g., cloning/chaining the method.)
     * @return array RegularExpression[]
     */
    public function getRegexps()
    {
        return $this->regexps;
    }

    /**
     * Returns the filtered stream.
     * The original stream is first read in fully, and the regex replace is performed.
     *
     * @param int $len Required $len for Reader compliance.
     *
     * @return mixed The filtered stream, or -1 if the end of the resulting stream has been reached.
     *
     * @exception IOException if the underlying stream throws an IOException
     * during reading
     */
    public function read($len = null)
    {

        $buffer = $this->in->read($len);

        if ($buffer === -1) {
            return -1;
        }

        // perform regex replace here ...
        foreach ($this->regexps as $exptype) {
            $regexp = $exptype->getRegexp($this->project);
            try {
                $buffer = $regexp->replace($buffer);
                $this->log(
                    "Performing regexp replace: /" . $regexp->getPattern() . "/" . $regexp->getReplace(
                    ) . "/g" . $regexp->getModifiers(),
                    Project::MSG_VERBOSE
                );
            } catch (Exception $e) {
                // perhaps mismatch in params (e.g. no replace or pattern specified)
                $this->log("Error performing regexp replace: " . $e->getMessage(), Project::MSG_WARN);
            }
        }

        return $buffer;
    }

    /**
     * Creates a new ReplaceRegExp filter using the passed in
     * Reader for instantiation.
     *
     * @param Reader $reader A Reader object providing the underlying stream.
     *                       Must not be <code>null</code>.
     *
     * @return ReplaceRegExp A new filter based on this configuration, but filtering
     *                       the specified reader
     */
    public function chain(Reader $reader)
    {
        $newFilter = new ReplaceRegExp($reader);
        $newFilter->setProject($this->getProject());
        $newFilter->setRegexps($this->getRegexps());

        return $newFilter;
    }

}
<?php

/*
 *  $Id: 69ad2e753a6efce00ea1ac34adf119d41294d676 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/filters/BaseParamFilterReader.php';
include_once 'phing/types/TokenSource.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * Replaces tokens in the original input with user-supplied values.
 *
 * Example:
 *
 * <pre><replacetokens begintoken="#" endtoken="#">;
 *   <token key="DATE" value="${TODAY}"/>
 * </replacetokens></pre>
 *
 * Or:
 *
 * <pre><filterreader classname="phing.filters.ReplaceTokens">
 *   <param type="tokenchar" name="begintoken" value="#"/>
 *   <param type="tokenchar" name="endtoken" value="#"/>
 *   <param type="token" name="DATE" value="${TODAY}"/>
 * </filterreader></pre>
 *
 * @author    <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a>
 * @author    hans lellelid, hans@velum.net
 * @version   $Id: 69ad2e753a6efce00ea1ac34adf119d41294d676 $
 * @see       BaseParamFilterReader
 * @package   phing.filters
 */
class ReplaceTokens extends BaseParamFilterReader implements ChainableReader
{

    /**
     * Default "begin token" character.
     * @var string
     */
    const DEFAULT_BEGIN_TOKEN = "@";

    /**
     * Default "end token" character.
     * @var string
     */
    const DEFAULT_END_TOKEN = "@";

    /**
     * [Deprecated] Data that must be read from, if not null.
     * @var string
     */
    private $_queuedData = null;

    /**
     * Array to hold the replacee-replacer pairs (String to String).
     * @var array
     */
    private $_tokens = array();

    /**
     * Array to hold the token sources that make tokens from
     * different sources available
     * @var array
     */
    private $_tokensources = array();

    /**
     * Array holding all tokens given directly to the Filter and
     * those passed via a TokenSource.
     * @var array
     */
    private $_alltokens = null;

    /**
     * Character marking the beginning of a token.
     * @var string
     */
    private $_beginToken = "@"; // self::DEFAULT_BEGIN_TOKEN;

    /**
     * Character marking the end of a token.
     * @var string
     */
    private $_endToken = "@"; //self::DEFAULT_END_TOKEN;

    /**
     * Performs lookup on key and returns appropriate replacement string.
     * @param  array  $matches Array of 1 el containing key to search for.
     * @return string Text with which to replace key or value of key if none is found.
     */
    private function replaceTokenCallback($matches)
    {

        $key = $matches[1];

        /* Get tokens from tokensource and merge them with the
         * tokens given directly via build file. This should be
         * done a bit more elegantly
         */
        if ($this->_alltokens === null) {
            $this->_alltokens = array();

            $count = count($this->_tokensources);
            for ($i = 0; $i < $count; $i++) {
                $source = $this->_tokensources[$i];
                $this->_alltokens = array_merge($this->_alltokens, $source->getTokens());
            }

            $this->_alltokens = array_merge($this->_tokens, $this->_alltokens);
        }

        $tokens = $this->_alltokens;

        $replaceWith = null;
        $count = count($tokens);

        for ($i = 0; $i < $count; $i++) {
            if ($tokens[$i]->getKey() === $key) {
                $replaceWith = $tokens[$i]->getValue();
            }
        }

        if ($replaceWith === null) {
            $replaceWith = $this->_beginToken . $key . $this->_endToken;
            $this->log("No token defined for key \"" . $this->_beginToken . $key . $this->_endToken . "\"");
        } else {
            $this->log(
                "Replaced \"" . $this->_beginToken . $key . $this->_endToken . "\" with \"" . $replaceWith . "\"",
                Project::MSG_VERBOSE
            );
        }

        return $replaceWith;
    }

    /**
     * Returns stream with tokens having been replaced with appropriate values.
     * If a replacement value is not found for a token, the token is left in the stream.
     *
     * @param null $len
     * @return mixed filtered stream, -1 on EOF.
     */
    public function read($len = null)
    {
        if (!$this->getInitialized()) {
            $this->_initialize();
            $this->setInitialized(true);
        }

        // read from next filter up the chain
        $buffer = $this->in->read($len);

        if ($buffer === -1) {
            return -1;
        }

        // filter buffer
        $buffer = preg_replace_callback(
            "/" . preg_quote($this->_beginToken, '/') . "([\w\.\-:]+?)" . preg_quote($this->_endToken, '/') . "/",
            array($this, 'replaceTokenCallback'),
            $buffer
        );

        return $buffer;
    }

    /**
     * Sets the "begin token" character.
     *
     * @param string $beginToken the character used to denote the beginning of a token.
     */
    public function setBeginToken($beginToken)
    {
        $this->_beginToken = (string) $beginToken;
    }

    /**
     * Returns the "begin token" character.
     *
     * @return string The character used to denote the beginning of a token.
     */
    public function getBeginToken()
    {
        return $this->_beginToken;
    }

    /**
     * Sets the "end token" character.
     *
     * @param string $endToken the character used to denote the end of a token
     */
    public function setEndToken($endToken)
    {
        $this->_endToken = (string) $endToken;
    }

    /**
     * Returns the "end token" character.
     *
     * @return the character used to denote the beginning of a token
     */
    public function getEndToken()
    {
        return $this->_endToken;
    }

    /**
     * Adds a token element to the map of tokens to replace.
     *
     * @return object The token added to the map of replacements.
     *                Must not be <code>null</code>.
     */
    public function createToken()
    {
        $num = array_push($this->_tokens, new Token());

        return $this->_tokens[$num - 1];
    }

    /**
     * Adds a token source to the sources of this filter.
     *
     * @return object A Reference to the source just added.
     */
    public function createTokensource()
    {
        $num = array_push($this->_tokensources, new TokenSource());

        return $this->_tokensources[$num - 1];
    }

    /**
     * Sets the map of tokens to replace.
     * ; used by ReplaceTokens::chain()
     *
     * @param $tokens
     * @throws Exception
     * @internal param A $array map (String->String) of token keys to replacement
     *              values. Must not be <code>null</code>.
     */
    public function setTokens($tokens)
    {
        // type check, error must never occur, bad code of it does
        if (!is_array($tokens)) {
            throw new Exception("Excpected 'array', got something else");
        }

        $this->_tokens = $tokens;
    }

    /**
     * Returns the map of tokens which will be replaced.
     * ; used by ReplaceTokens::chain()
     *
     * @return array A map (String->String) of token keys to replacement values.
     */
    public function getTokens()
    {
        return $this->_tokens;
    }

    /**
     * Sets the tokensources to use; used by ReplaceTokens::chain()
     *
     * @param $sources
     * @throws Exception
     * @internal param An $array array of token sources.
     */
    public function setTokensources($sources)
    {
        // type check
        if (!is_array($sources)) {
            throw new Exception("Exspected 'array', got something else");
        }
        $this->_tokensources = $sources;
    }

    /**
     * Returns the token sources used by this filter; used by ReplaceTokens::chain()
     *
     * @return array
     */
    public function getTokensources()
    {
        return $this->_tokensources;
    }

    /**
     * Creates a new ReplaceTokens using the passed in
     * Reader for instantiation.
     *
     * @param Reader $reader
     * @throws Exception
     * @internal param A $object Reader object providing the underlying stream.
     *               Must not be <code>null</code>.
     *
     * @return object A new filter based on this configuration, but filtering
     *                the specified reader
     */
    public function chain(Reader $reader)
    {
        $newFilter = new ReplaceTokens($reader);
        $newFilter->setProject($this->getProject());
        $newFilter->setBeginToken($this->getBeginToken());
        $newFilter->setEndToken($this->getEndToken());
        $newFilter->setTokens($this->getTokens());
        $newFilter->setTokensources($this->getTokensources());
        $newFilter->setInitialized(true);

        return $newFilter;
    }

    /**
     * Initializes tokens and loads the replacee-replacer hashtable.
     * This method is only called when this filter is used through
     * a <filterreader> tag in build file.
     */
    private function _initialize()
    {
        $params = $this->getParameters();
        if ($params !== null) {
            for ($i = 0; $i < count($params); $i++) {
                if ($params[$i] !== null) {
                    $type = $params[$i]->getType();
                    if ($type === "tokenchar") {
                        $name = $params[$i]->getName();
                        if ($name === "begintoken") {
                            $this->_beginToken = substr($params[$i]->getValue(), 0, strlen($params[$i]->getValue()));
                        } else {
                            if ($name === "endtoken") {
                                $this->_endToken = substr($params[$i]->getValue(), 0, strlen($params[$i]->getValue()));
                            }
                        }
                    } else {
                        if ($type === "token") {
                            $name = $params[$i]->getName();
                            $value = $params[$i]->getValue();

                            $tok = new Token();
                            $tok->setKey($name);
                            $tok->setValue($value);

                            array_push($this->_tokens, $tok);
                        } else {
                            if ($type === "tokensource") {
                                // Store data from nested tags in local array
                                $arr = array();

                                $subparams = $params[$i]->getParams();
                                foreach ($subparams as $subparam) {
                                    $arr[$subparam->getName()] = $subparam->getValue();
                                }

                                // Create TokenSource
                                $tokensource = new TokenSource();
                                if (isset($arr["classname"])) {
                                    $tokensource->setClassname($arr["classname"]);
                                }

                                // Copy other parameters 1:1 to freshly created TokenSource
                                foreach ($arr as $key => $value) {
                                    if (strtolower($key) === "classname") {
                                        continue;
                                    }
                                    $param = $tokensource->createParam();
                                    $param->setName($key);
                                    $param->setValue($value);
                                }

                                $this->_tokensources[] = $tokensource;
                            }
                        }
                    }
                }
            }
        }
    }
}

/**
 * Holds a token.
 *
 * @package   phing.filters
 */
class Token
{

    /**
     * Token key.
     * @var string
     */
    private $_key;

    /**
     * Token value.
     * @var string
     */
    private $_value;

    /**
     * Sets the token key.
     *
     * @param string $key The key for this token. Must not be <code>null</code>.
     */
    public function setKey($key)
    {
        $this->_key = (string) $key;
    }

    /**
     * Sets the token value.
     *
     * @param string $value The value for this token. Must not be <code>null</code>.
     */
    public function setValue($value)
    {
        // special case for boolean values
        if (is_bool($value)) {
            if ($value) {
                $this->_value = "true";
            } else {
                $this->_value = "false";
            }
        } else {
            $this->_value = (string) $value;
        }
    }

    /**
     * Returns the key for this token.
     *
     * @return string The key for this token.
     */
    public function getKey()
    {
        return $this->_key;
    }

    /**
     * Returns the value for this token.
     *
     * @return string The value for this token.
     */
    public function getValue()
    {
        return $this->_value;
    }

    /**
     * Sets the token value from text.
     *
     * @param string $value The value for this token. Must not be <code>null</code>.
     */
    public function addText($value)
    {
        $this->setValue($value);
    }
}
<?php

/*
 *  $Id: 2ce2fe6389844147fcaa7f85308157891a38e7af $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/filters/BaseParamFilterReader.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * Replaces tokens in the original input with the contents of a file.
 * The file to be used is controlled by the name of the token which
 * corresponds to the basename of the file to be used together with
 * the optional pre and postfix strings that is possible to set.
 *
 * By default all HTML entities in the file is replaced by the
 * corresponding HTML entities. This behaviour can be controlled by
 * the "translatehtml" parameter.
 *
 * Supported parameters are:
 *  <pre>
 *  prefix         string Text to be prefixed to token before using as filename
 *  postfix        string Text to be prefixed to token before using as filename
 *  dir            string The directory where the files should be read from
 *  translatehtml  bool   If we should translate all HTML entities in the file.
 * </pre>
 * Example:
 *
 * <pre><filterreader classname="phing.filters.ReplaceTokensWithFile">
 *   <param name="dir" value="examples/" />
 *   <param name="postfix" value=".php" />
 * </filterreader></pre>
 *
 * @author    johan persson, johanp@aditus.nu
 * @version   $Id: 2ce2fe6389844147fcaa7f85308157891a38e7af $
 * @see       ReplaceTokensWithFile
 * @package   phing.filters
 */
class ReplaceTokensWithFile extends BaseParamFilterReader implements ChainableReader
{

    /**
     * Default "begin token" character.
     * @var string
     */
    const DEFAULT_BEGIN_TOKEN = "#@#";

    /**
     * Default "end token" character.
     * @var string
     */
    const DEFAULT_END_TOKEN = "#@#";

    /**
     * Array to hold the token sources that make tokens from
     * different sources available
     * @var array
     */
    private $_tokensources = array();

    /**
     * Character marking the beginning of a token.
     * @var string
     */
    private $_beginToken = ReplaceTokensWithFile::DEFAULT_BEGIN_TOKEN;

    /**
     * Character marking the end of a token.
     * @var string
     */
    private $_endToken = ReplaceTokensWithFile::DEFAULT_END_TOKEN;

    /**
     * File prefix to be inserted in front of the token to create the
     * file name to be used.
     * @var string
     */
    private $_prefix = '';

    /**
     * File postfix to be inserted in front of the token to create the
     * file name to be used.
     * @var string
     */
    private $_postfix = '';

    /**
     * Directory where to look for the files. The default is to look in the
     * current file.
     *
     * @var string
     */
    private $_dir = './';

    /**
     * Translate all HTML entities in the file to the corresponding HTML
     * entities before it is used as replacements. For example all '<'
     * will be translated to &lt; before the content is inserted.
     *
     * @var boolean
     */
    private $_translatehtml = true;

    /**
     * Sets the drectory where to look for the files to use for token replacement
     *
     * @param $translate
     * @internal param string $dir
     */
    public function setTranslateHTML($translate)
    {
        $this->_translatehtml = (bool) $translate;
    }

    /**
     * Returns the drectory where to look for the files to use for token replacement
     */
    public function getTranslateHTML()
    {
        return $this->_translatehtml;
    }

    /**
     * Sets the drectory where to look for the files to use for token replacement
     *
     * @param string $dir
     */
    public function setDir($dir)
    {
        $this->_dir = (string) $dir;
    }

    /**
     * Returns the drectory where to look for the files to use for token replacement
     */
    public function getDir()
    {
        return $this->_dir;
    }

    /**
     * Sets the prefix that is prepended to the token in order to create the file
     * name. For example if the token is 01 and the prefix is "example" then
     * the filename to look for will be "example01"
     *
     * @param string $prefix
     */
    public function setPrefix($prefix)
    {
        $this->_prefix = (string) $prefix;
    }

    /*
     * Returns the prefix that is prepended to the token in order to create the file
     * name. For example if the token is 01 and the prefix is "example" then
     * the filename to look for will be "example01"
     */
    /**
     * @return string
     */
    public function getPrefix()
    {
        return $this->_prefix;
    }

    /**
     * Sets the postfix that is added to the token in order to create the file
     * name. For example if the token is 01 and the postfix is ".php" then
     * the filename to look for will be "01.php"
     *
     * @param string $postfix
     */
    public function setPostfix($postfix)
    {
        $this->_postfix = (string) $postfix;
    }

    /**
     * Returns the postfix that is added to the token in order to create the file
     * name. For example if the token is 01 and the postfix is ".php" then
     * the filename to look for will be "01.php"
     */
    public function getPostfix()
    {
        return $this->_postfix;
    }

    /**
     * Sets the "begin token" character.
     *
     * @param string $beginToken the character used to denote the beginning of a token.
     */
    public function setBeginToken($beginToken)
    {
        $this->_beginToken = (string) $beginToken;
    }

    /**
     * Returns the "begin token" character.
     *
     * @return string The character used to denote the beginning of a token.
     */
    public function getBeginToken()
    {
        return $this->_beginToken;
    }

    /**
     * Sets the "end token" character.
     *
     * @param string $endToken the character used to denote the end of a token
     */
    public function setEndToken($endToken)
    {
        $this->_endToken = (string) $endToken;
    }

    /**
     * Returns the "end token" character.
     *
     * @return the character used to denote the beginning of a token
     */
    public function getEndToken()
    {
        return $this->_endToken;
    }

    /**
     * Replace the token found with the appropriate file contents
     * @param  array  $matches Array of 1 el containing key to search for.
     * @return string Text with which to replace key or value of key if none is found.
     */
    private function replaceTokenCallback($matches)
    {

        $filetoken = $matches[1];

        // We look in all specified directories for the named file and use
        // the first directory which has the file.
        $dirs = explode(';', $this->_dir);

        $ndirs = count($dirs);
        $n = 0;
        $file = $dirs[$n] . $this->_prefix . $filetoken . $this->_postfix;

        while ($n < $ndirs && !is_readable($file)) {
            ++$n;
        }

        if (!is_readable($file) || $n >= $ndirs) {
            $this->log(
                "Can not read or find file \"$file\". Searched in directories: {$this->_dir}",
                Project::MSG_WARN
            );
            //return $this->_beginToken  . $filetoken . $this->_endToken;
            return "[Phing::Filters::ReplaceTokensWithFile: Can not find file " . '"' . $filetoken . $this->_postfix . '"' . "]";
        }

        $buffer = file_get_contents($file);
        if ($this->_translatehtml) {
            $buffer = htmlentities($buffer);
        }

        if ($buffer === null) {
            $buffer = $this->_beginToken . $filetoken . $this->_endToken;
            $this->log("No corresponding file found for key \"$buffer\"", Project::MSG_WARN);
        } else {
            $this->log(
                "Replaced \"" . $this->_beginToken . $filetoken . $this->_endToken . "\" with content from file \"$file\""
            );
        }

        return $buffer;
    }

    /**
     * Returns stream with tokens having been replaced with appropriate values.
     * If a replacement value is not found for a token, the token is left in the stream.
     *
     * @param null $len
     * @return mixed filtered stream, -1 on EOF.
     */
    public function read($len = null)
    {
        if (!$this->getInitialized()) {
            $this->_initialize();
            $this->setInitialized(true);
        }

        // read from next filter up the chain
        $buffer = $this->in->read($len);

        if ($buffer === -1) {
            return -1;
        }

        // filter buffer
        $buffer = preg_replace_callback(
            "$" . preg_quote($this->_beginToken) . "([\w\.\-:\/]+?)" . preg_quote($this->_endToken) . "$",
            array($this, 'replaceTokenCallback'),
            $buffer
        );

        return $buffer;
    }

    /**
     * Creates a new ReplaceTokensWithFile using the passed in
     * Reader for instantiation.
     *
     * @param Reader $reader
     * @internal param A $object Reader object providing the underlying stream.
     *               Must not be <code>null</code>.
     *
     * @return object A new filter based on this configuration, but filtering
     *                the specified reader
     */
    public function chain(Reader $reader)
    {
        $newFilter = new ReplaceTokensWithFile($reader);
        $newFilter->setProject($this->getProject());
        $newFilter->setTranslateHTML($this->getTranslateHTML());
        $newFilter->setDir($this->getDir());
        $newFilter->setPrefix($this->getPrefix());
        $newFilter->setPostfix($this->getPostfix());
        $newFilter->setBeginToken($this->getBeginToken());
        $newFilter->setEndToken($this->getEndToken());
        $newFilter->setInitialized(true);

        return $newFilter;
    }

    /**
     * Initializes parameters
     * This method is only called when this filter is used through
     * a <filterreader> tag in build file.
     */
    private function _initialize()
    {
        $params = $this->getParameters();
        $n = count($params);

        if ($params !== null) {
            for ($i = 0; $i < $n; $i++) {
                if ($params[$i] !== null) {
                    $name = $params[$i]->getName();
                    switch ($name) {
                        case 'begintoken' :
                            $this->_beginToken = $params[$i]->getValue();
                            break;
                        case 'endtoken' :
                            $this->_endToken = $params[$i]->getValue();
                            break;
                        case 'dir':
                            $this->_dir = $params[$i]->getValue();
                            break;
                        case 'prefix':
                            $this->_prefix = $params[$i]->getValue();
                            break;
                        case 'postfix':
                            $this->_postfix = $params[$i]->getValue();
                            break;
                        case 'translatehtml':
                            $this->_translatehtml = $params[$i]->getValue();
                            break;
                    }
                }
            }
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/filters/BaseParamFilterReader.php';
include_once 'phing/util/StringHelper.php';

/**
 * <p>
 * Sort a file before and/or after the file.
 * </p>
 *
 * <p>
 * Examples:
 * </p>
 *
 * <pre>
 *   &lt;copy todir=&quot;build&quot;&gt;
 *       &lt;fileset dir=&quot;input&quot; includes=&quot;*.txt&quot;/&gt;
 *       &lt;filterchain&gt;
 *           &lt;sortfilter/&gt;
 *       &lt;/filterchain&gt;
 *   &lt;/copy&gt;
 * </pre>
 *
 * <p>
 * Sort all files <code>*.txt</code> from <i>src</i> location and copy
 * them into <i>build</i> location. The lines of each file are sorted
 * in ascendant order comparing the lines.
 * </p>
 *
 * <pre>
 *   &lt;copy todir=&quot;build&quot;&gt;
 *       &lt;fileset dir=&quot;input&quot; includes=&quot;*.txt&quot;/&gt;
 *       &lt;filterchain&gt;
 *           &lt;sortfilter reverse=&quot;true&quot;/&gt;
 *       &lt;/filterchain&gt;
 *   &lt;/copy&gt;
 * </pre>
 *
 * <p>
 * Sort all files <code>*.txt</code> from <i>src</i> location into reverse
 * order and copy them into <i>build</i> location. If reverse parameter has
 * value <code>true</code> (default value), then the output line of the files
 * will be in ascendant order.
 * </p>
 *
 * @author    Siad.ardroumli <siad.ardroumli@gmail.com>
 *
 * @see       BaseParamFilterReader
 *
 * @package   phing.filters
 */
class SortFilter extends BaseParamFilterReader implements ChainableReader
{
    /** Parameter name for reverse order. */
    private static $REVERSE_KEY = "reverse";

    /**
     * Controls if the sorting process will be in ascendant/descendant order. If
     * If has value <code>true</code>, then the line of the file will be
     * sorted on descendant order. Default value: <code>false</code>. It will
     * be considered only if <code>comparator</code> is <code>null</code>.
     */
    private $reverse;

    /**
     * Stores the lines to be sorted.
     */
    private $lines;

    /**
     * Creates a new filtered reader.
     *
     * @param Reader $in
     *            A Reader object providing the underlying stream. Must not be
     *            <code>null</code>.
     */
    public function __construct(Reader $in = null)
    {
        parent::__construct($in);
    }

    /**
     * Returns the next character in the filtered stream. If the desired number
     * of lines have already been read, the resulting stream is effectively at
     * an end. Otherwise, the next character from the underlying stream is read
     * and returned.
     *
     * @param int $len
     * @return string the next character in the resulting stream, or -1 if the end of
     *         the resulting stream has been reached
     * @throws BuildException
     * @exception IOException
     *                if the underlying stream throws an IOException during
     *                reading
     */
    public function read($len = null)
    {
        if (!$this->getInitialized()) {
            $this->initialize();
            $this->setInitialized(true);
        }

        $buffer = $this->in->read($len);

        if ($buffer === -1) {
            return -1;
        }

        $this->lines = explode("\n", $buffer);

        $this->sort();

        $filtered_buffer = implode("\n", $this->lines);

        return $filtered_buffer;
    }

    /**
     * Creates a new SortReader using the passed in Reader for instantiation.
     *
     * @param Reader $rdr
     *            A Reader object providing the underlying stream. Must not be
     *            <code>null</code>.
     *
     * @return SortFilter a new filter based on this configuration, but filtering the
     *         specified reader
     */
    public function chain(Reader $rdr)
    {
        $newFilter = new SortFilter($rdr);
        $newFilter->setReverse($this->isReverse());
        $newFilter->setInitialized(true);
        return $newFilter;
    }

    /**
     * Returns <code>true</code> if the sorting process will be in reverse
     * order, otherwise the sorting process will be in ascendant order.
     *
     * @return boolean <code>true</code> if the sorting process will be in reverse
     *                 order, otherwise the sorting process will be in ascendant order.
     */
    public function isReverse()
    {
        return $this->reverse;
    }

    /**
     * Sets the sorting process will be in ascendant (<code>reverse=false</code>)
     * or to descendant (<code>reverse=true</code>).
     *
     * @param boolean $reverse
     *            Boolean representing reverse ordering process.
     */
    public function setReverse($reverse)
    {
        $this->reverse = $reverse;
    }

    /**
     * Scans the parameters list
     */
    private function initialize()
    {
        // get parameters
        $params = $this->getParameters();

        foreach ($params as $param) {
            $paramName = $param->getName();
            if (self::$REVERSE_KEY === $paramName) {
                $this->setReverse(StringHelper::booleanValue($param->getValue()));
                continue;
            }
        }
    }

    /**
     * Sorts the read lines (<code>$this->lines</code>) according to the sorting
     * criteria defined by the user.
     */
    private function sort()
    {
        if ($this->reverse) {
            rsort($this->lines);
        } else {
            sort($this->lines);
        }
    }
}
<?php

/*
 *  $Id: dab4c38d630ec372a9a4aecf9284a94a895812d9 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/filters/BaseParamFilterReader.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * Filter to flatten the stream to a single line.
 *
 * Example:
 *
 * <pre><striplinebreaks/></pre>
 *
 * Or:
 *
 * <pre><filterreader classname="phing.filters.StripLineBreaks"/></pre>
 *
 * @author    <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a>
 * @author    hans lellelid, hans@velum.net
 * @version   $Id: dab4c38d630ec372a9a4aecf9284a94a895812d9 $
 * @see       BaseParamFilterReader
 * @package   phing.filters
 */
class StripLineBreaks extends BaseParamFilterReader implements ChainableReader
{

    /**
     * Default line-breaking characters.
     * @var string
     */
    const DEFAULT_LINE_BREAKS = "\r\n";

    /**
     * Parameter name for the line-breaking characters parameter.
     * @var string
     */
    const LINES_BREAKS_KEY = "linebreaks";

    /**
     * The characters that are recognized as line breaks.
     * @var string
     */
    private $_lineBreaks = "\r\n"; // self::DEFAULT_LINE_BREAKS;

    /**
     * Returns the filtered stream, only including
     * characters not in the set of line-breaking characters.
     *
     * @param null $len
     * @return mixed the resulting stream, or -1
     *               if the end of the resulting stream has been reached.
     *
     * @exception IOException if the underlying stream throws an IOException
     *            during reading
     */
    public function read($len = null)
    {
        if (!$this->getInitialized()) {
            $this->_initialize();
            $this->setInitialized(true);
        }

        $buffer = $this->in->read($len);
        if ($buffer === -1) {
            return -1;
        }

        $buffer = preg_replace("/[" . $this->_lineBreaks . "]/", '', $buffer);

        return $buffer;
    }

    /**
     * Sets the line-breaking characters.
     *
     * @param string $lineBreaks A String containing all the characters to be
     *                           considered as line-breaking.
     */
    public function setLineBreaks($lineBreaks)
    {
        $this->_lineBreaks = (string) $lineBreaks;
    }

    /**
     * Gets the line-breaking characters.
     *
     * @return string A String containing all the characters that are considered as line-breaking.
     */
    public function getLineBreaks()
    {
        return $this->_lineBreaks;
    }

    /**
     * Creates a new StripLineBreaks using the passed in
     * Reader for instantiation.
     *
     * @param Reader $reader
     * @internal param A $object Reader object providing the underlying stream.
     *               Must not be <code>null</code>.
     *
     * @return object A new filter based on this configuration, but filtering
     *                the specified reader
     */
    public function chain(Reader $reader)
    {
        $newFilter = new StripLineBreaks($reader);
        $newFilter->setLineBreaks($this->getLineBreaks());
        $newFilter->setInitialized(true);
        $newFilter->setProject($this->getProject());

        return $newFilter;
    }

    /**
     * Parses the parameters to set the line-breaking characters.
     */
    private function _initialize()
    {
        $userDefinedLineBreaks = null;
        $params = $this->getParameters();
        if ($params !== null) {
            for ($i = 0; $i < count($params); $i++) {
                if (self::LINES_BREAKS_KEY === $params[$i]->getName()) {
                    $userDefinedLineBreaks = $params[$i]->getValue();
                    break;
                }
            }
        }

        if ($userDefinedLineBreaks !== null) {
            $this->_lineBreaks = $userDefinedLineBreaks;
        }
    }
}
<?php

/*
 *  $Id: 8761015c0828acd83c3eeec2bac42e09125bd410 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/filters/BaseParamFilterReader.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * This filter strips line comments.
 *
 * Example:
 *
 * <pre><striplinecomments>
 *   <comment value="#"/>
 *   <comment value="--"/>
 *   <comment value="REM "/>
 *   <comment value="rem "/>
 *   <comment value="//"/>
 * </striplinecomments></pre>
 *
 * Or:
 *
 * <pre><filterreader classname="phing.filters.StripLineComments">
 *   <param type="comment" value="#"/>
 *   <param type="comment" value="--"/>
 *   <param type="comment" value="REM "/>
 *   <param type="comment" value="rem "/>
 *   <param type="comment" value="//"/>
 * </filterreader></pre>
 *
 * @author    <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a>
 * @author    hans lellelid, hans@velum.net
 * @version   $Id: 8761015c0828acd83c3eeec2bac42e09125bd410 $
 * @see       BaseParamFilterReader
 * @package   phing.filters
 */
class StripLineComments extends BaseParamFilterReader implements ChainableReader
{

    /** Parameter name for the comment prefix. */
    const COMMENTS_KEY = "comment";

    /** Array that holds the comment prefixes. */
    private $_comments = array();

    /**
     * Returns stream only including
     * lines from the original stream which don't start with any of the
     * specified comment prefixes.
     *
     * @param null $len
     * @return mixed the resulting stream, or -1
     *               if the end of the resulting stream has been reached.
     *
     */
    public function read($len = null)
    {

        if (!$this->getInitialized()) {
            $this->_initialize();
            $this->setInitialized(true);
        }

        $buffer = $this->in->read($len);

        if ($buffer === -1) {
            return -1;
        }

        $lines = explode("\n", $buffer);
        $filtered = array();

        $commentsSize = count($this->_comments);

        foreach ($lines as $line) {
            for ($i = 0; $i < $commentsSize; $i++) {
                $comment = $this->_comments[$i]->getValue();
                if (StringHelper::startsWith($comment, ltrim($line))) {
                    $line = null;
                    break;
                }
            }
            if ($line !== null) {
                $filtered[] = $line;
            }
        }

        $filtered_buffer = implode("\n", $filtered);

        return $filtered_buffer;
    }

    /*
     * Adds a <code>comment</code> element to the list of prefixes.
     *
     * @return comment The <code>comment</code> element added to the
     *                 list of comment prefixes to strip.
    */
    public function createComment()
    {
        $num = array_push($this->_comments, new Comment());

        return $this->_comments[$num - 1];
    }

    /*
     * Sets the list of comment prefixes to strip.
     *
     * @param comments A list of strings, each of which is a prefix
     *                 for a comment line. Must not be <code>null</code>.
    */
    /**
     * @param $lineBreaks
     * @throws Exception
     */
    public function setComments($lineBreaks)
    {
        if (!is_array($lineBreaks)) {
            throw new Exception("Excpected 'array', got something else");
        }
        $this->_comments = $lineBreaks;
    }

    /*
     * Returns the list of comment prefixes to strip.
     *
     * @return array The list of comment prefixes to strip.
    */
    /**
     * @return array
     */
    public function getComments()
    {
        return $this->_comments;
    }

    /*
     * Creates a new StripLineComments using the passed in
     * Reader for instantiation.
     *
     * @param reader A Reader object providing the underlying stream.
     *               Must not be <code>null</code>.
     *
     * @return a new filter based on this configuration, but filtering
     *           the specified reader
     */
    /**
     * @param Reader $reader
     * @return StripLineComments
     * @throws Exception
     */
    public function chain(Reader $reader)
    {
        $newFilter = new StripLineComments($reader);
        $newFilter->setComments($this->getComments());
        $newFilter->setInitialized(true);
        $newFilter->setProject($this->getProject());

        return $newFilter;
    }

    /*
     * Parses the parameters to set the comment prefixes.
    */
    private function _initialize()
    {
        $params = $this->getParameters();
        if ($params !== null) {
            for ($i = 0; $i < count($params); $i++) {
                if (self::COMMENTS_KEY === $params[$i]->getType()) {
                    $comment = new Comment();
                    $comment->setValue($params[$i]->getValue());
                    array_push($this->_comments, $comment);
                }
            }
        }
    }
}

/**
 * The class that holds a comment representation.
 *
 * @package phing.filters
 */
class Comment
{

    /** The prefix for a line comment. */
    private $_value;

    /*
     * Sets the prefix for this type of line comment.
     *
     * @param string $value The prefix for a line comment of this type.
     *                      Must not be <code>null</code>.
     */
    /**
     * @param $value
     */
    public function setValue($value)
    {
        $this->_value = (string) $value;
    }

    /*
     * Returns the prefix for this type of line comment.
     *
     * @return string The prefix for this type of line comment.
    */
    public function getValue()
    {
        return $this->_value;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */
include_once 'phing/filters/BaseFilterReader.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * This is a Php comment and string stripper reader that filters
 * those lexical tokens out for purposes of simple Php parsing.
 * (if you have more complex Php parsing needs, use a real lexer).
 * Since this class heavily relies on the single char read function,
 * you are recommended to make it work on top of a buffered reader.
 *
 * @author    <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a>
 * @author    hans lellelid, hans@velum.net
 * @version   $Id: e46d7849417c2b8249f317003a3ebc161fac0e96 $
 * @see       FilterReader
 * @package   phing.filters
 */
class StripPhpComments extends BaseFilterReader implements ChainableReader
{
    /**
     * The read-ahead character, used for effectively pushing a single
     * character back. -1 indicates that no character is in the buffer.
     */
    private $_readAheadCh = -1;

    /**
     * Whether or not the parser is currently in the middle of a string
     * literal.
     * @var boolean
     */
    private $_inString = false;

    /**
     * Returns the  stream without Php comments.
     *
     * @param null $len
     * @return string the resulting stream, or -1
     *             if the end of the resulting stream has been reached
     *
     */
    public function read($len = null)
    {

        $buffer = $this->in->read($len);
        if ($buffer === -1) {
            return -1;
        }
        $newStr = '';
        $tokens = token_get_all($buffer);

        foreach ($tokens as $token) {
            if (is_array($token)) {
                list($id, $text) = $token;

                switch ($id) {
                    case T_COMMENT:
                    case T_DOC_COMMENT:
                        // no action on comments
                        continue 2;

                    default:
                        $newStr .= $text;
                        continue 2;
                }
            }
            $newStr .= $token;
        }

        return $newStr;
    }

    /**
     * Returns the next character in the filtered stream, not including
     * Php comments.
     *
     * @return int the next character in the resulting stream, or -1
     *             if the end of the resulting stream has been reached
     *
     * @throws IOException if the underlying stream throws an IOException
     *                     during reading
     * @deprecated
     */
    public function readChar()
    {
        $ch = -1;

        if ($this->_readAheadCh !== -1) {
            $ch = $this->_readAheadCh;
            $this->_readAheadCh = -1;
        } else {
            $ch = $this->in->readChar();
            if ($ch === "\"") {
                $this->_inString = !$this->_inString;
            } else {
                if (!$this->_inString) {
                    if ($ch === "/") {
                        $ch = $this->in->readChar();
                        if ($ch === "/") {
                            while ($ch !== "\n" && $ch !== -1) {
                                $ch = $this->in->readChar();
                            }
                        } else {
                            if ($ch === "*") {
                                while ($ch !== -1) {
                                    $ch = $this->in->readChar();
                                    while ($ch === "*" && $ch !== -1) {
                                        $ch = $this->in->readChar();
                                    }

                                    if ($ch === "/") {
                                        $ch = $this->readChar();
                                        echo "$ch\n";
                                        break;
                                    }
                                }
                            } else {
                                $this->_readAheadCh = $ch;
                                $ch = "/";
                            }
                        }
                    }
                }
            }
        }

        return $ch;
    }

    /**
     * Creates a new StripPhpComments using the passed in
     * Reader for instantiation.
     *
     * @param A|Reader $reader
     * @internal param A $reader Reader object providing the underlying stream.
     *               Must not be <code>null</code>.
     *
     * @return $this a new filter based on this configuration, but filtering
     *           the specified reader
     */
    public function chain(Reader $reader)
    {
        $newFilter = new StripPhpComments($reader);
        $newFilter->setProject($this->getProject());

        return $newFilter;
    }
}
<?php

/*
 *  $Id $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/filters/BaseFilterReader.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * Strips whitespace from [php] files using PHP stripwhitespace() method.
 *
 * @author    Hans Lellelid, hans@velum.net
 * @version   $Id: 9e156ae0693bd973b8e0f89490814171365197da $
 * @see       FilterReader
 * @package   phing.filters
 * @todo -c use new PHP functions to perform this instead of regex.
 */
class StripWhitespace extends BaseFilterReader implements ChainableReader
{

    private $processed = false;

    /**
     * Returns the  stream without Php comments and whitespace.
     *
     * @param null $len
     * @return the resulting stream, or -1
     *             if the end of the resulting stream has been reached
     *
     */
    public function read($len = null)
    {

        if ($this->processed === true) {
            return -1; // EOF
        }

        // Read XML
        $php = null;
        while (($buffer = $this->in->read($len)) !== -1) {
            $php .= $buffer;
        }

        if ($php === null) { // EOF?

            return -1;
        }

        if (empty($php)) {
            $this->log("PHP file is empty!", Project::MSG_WARN);

            return ''; // return empty string, don't attempt to strip whitespace
        }

        // write buffer to a temporary file, since php_strip_whitespace() needs a filename
        $file = new PhingFile(tempnam(PhingFile::getTempDir(), 'stripwhitespace'));
        file_put_contents($file->getAbsolutePath(), $php);
        $output = php_strip_whitespace($file->getAbsolutePath());
        unlink($file->getAbsolutePath());

        $this->processed = true;

        return $output;
    }

    /**
     * Creates a new StripWhitespace using the passed in
     * Reader for instantiation.
     *
     * @param A|Reader $reader
     * @internal param A $reader Reader object providing the underlying stream.
     *               Must not be <code>null</code>.
     *
     * @return a new filter based on this configuration, but filtering
     *           the specified reader
     */
    public function chain(Reader $reader)
    {
        $newFilter = new StripWhitespace($reader);
        $newFilter->setProject($this->getProject());

        return $newFilter;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/filters/BaseParamFilterReader.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * Attaches a suffix to every line.
 *
 * Example:
 * <pre><suffixlines suffix="Foo"/></pre>
 *
 * Or:
 *
 * <pre><filterreader classname="phing.filters.SuffixLines">
 *  <param name="suffix" value="Foo"/>
 * </filterreader></pre>
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @see       FilterReader
 * @package   phing.filters
 */
class SuffixLines extends BaseParamFilterReader implements ChainableReader
{
    /**
     * Parameter name for the suffix.
     * @var string
     */
    const SUFFIX_KEY = "suffix";

    /**
     * The suffix to be used.
     * @var string
     */
    private $suffix = null;

    /**
     * Adds a suffix to each line of input stream and returns resulting stream.
     *
     * @param null $len
     * @return mixed buffer, -1 on EOF
     */
    public function read($len = null)
    {
        if (!$this->getInitialized()) {
            $this->_initialize();
            $this->setInitialized(true);
        }

        $buffer = $this->in->read($len);

        if ($buffer === -1) {
            return -1;
        }
        $lines = preg_split("~\R~", $buffer);
        $filtered = array();

        foreach ($lines as $line) {
            $filtered[] = $line . $this->suffix;
        }

        $filtered_buffer = implode(PHP_EOL, $filtered);

        return $filtered_buffer;
    }

    /**
     * Sets the suffix to add at the end of each input line.
     *
     * @param string $suffix The suffix to add at the start of each input line.
     *                       May be <code>null</code>, in which case no suffix
     *                       is added.
     */
    public function setSuffix($suffix)
    {
        $this->suffix = (string) $suffix;
    }

    /**
     * Returns the suffix which will be added at the end of each input line.
     *
     * @return string The suffix which will be added at the end of each input line
     */
    public function getSuffix()
    {
        return $this->suffix;
    }

    /**
     * Creates a new PrefixLines filter using the passed in
     * Reader for instantiation.
     *
     * @param Reader $reader
     * @internal param A $object Reader object providing the underlying stream.
     *               Must not be <code>null</code>.
     *
     * @return object A new filter based on this configuration, but filtering
     *                the specified reader
     */
    public function chain(Reader $reader)
    {
        $newFilter = new SuffixLines($reader);
        $newFilter->setSuffix($this->getSuffix());
        $newFilter->setInitialized(true);
        $newFilter->setProject($this->getProject());

        return $newFilter;
    }

    /**
     * Initializes the suffix if it is available from the parameters.
     */
    private function _initialize()
    {
        $params = $this->getParameters();
        if ($params !== null) {
            for ($i = 0, $_i = count($params); $i < $_i; $i++) {
                if (self::SUFFIX_KEY == $params[$i]->getName()) {
                    $this->suffix = (string) $params[$i]->getValue();
                    break;
                }
            }
        }
    }
}
<?php

/*
 *  $Id: 55c4ddd8825bb8912804163ddbc825abb4bc5aba $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

require_once 'phing/filters/BaseParamFilterReader.php';
require_once 'phing/filters/ChainableReader.php';

/**
 * Converts tabs to spaces.
 *
 * Example:
 *
 * <pre><tabtospaces tablength="8"></pre>
 *
 * Or:
 *
 * <pre><filterreader classname="phing.filters.TabsToSpaces">
 *   <param name="tablength" value="8">
 * </filterreader></pre>
 *
 * @author    Yannick Lecaillez <yl@seasonfive.com>
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: 55c4ddd8825bb8912804163ddbc825abb4bc5aba $
 * @see       BaseParamFilterReader
 * @package   phing.filters
 */
class TabToSpaces extends BaseParamFilterReader implements ChainableReader
{

    /**
     * The default tab length.
     * @var int
     */
    const DEFAULT_TAB_LENGTH = 8;

    /**
     * Parameter name for the length of a tab.
     * @var string
     */
    const TAB_LENGTH_KEY = "tablength";

    /**
     * Tab length in this filter.
     * @var int
     */
    private $tabLength = 8; //self::DEFAULT_TAB_LENGTH;

    /**
     * Returns stream after converting tabs to the specified number of spaces.
     *
     * @param null $len
     * @return the resulting stream, or -1
     *             if the end of the resulting stream has been reached
     *
     * @exception IOException if the underlying stream throws an IOException
     *            during reading
     */
    public function read($len = null)
    {

        if (!$this->getInitialized()) {
            $this->_initialize();
            $this->setInitialized(true);
        }

        $buffer = $this->in->read($len);

        if ($buffer === -1) {
            return -1;
        }

        $buffer = str_replace("\t", str_repeat(' ', $this->tabLength), $buffer);

        return $buffer;
    }

    /**
     * Sets the tab length.
     *
     * @param int $tabLength The number of spaces to be used when converting a tab.
     */
    public function setTablength($tabLength)
    {
        $this->tabLength = (int) $tabLength;
    }

    /**
     * Returns the tab length.
     *
     * @return int The number of spaces used when converting a tab
     */
    public function getTablength()
    {
        return $this->tabLength;
    }

    /**
     * Creates a new TabsToSpaces using the passed in
     * Reader for instantiation.
     *
     * @param Reader $reader A Reader object providing the underlying stream.
     *                       Must not be <code>null</code>.
     *
     * @return Reader A new filter based on this configuration, but filtering
     *                the specified reader
     */
    public function chain(Reader $reader)
    {
        $newFilter = new TabToSpaces($reader);
        $newFilter->setTablength($this->getTablength());
        $newFilter->setInitialized(true);
        $newFilter->setProject($this->getProject());

        return $newFilter;
    }

    /**
     * Parses the parameters to set the tab length.
     */
    private function _initialize()
    {
        $params = $this->getParameters();
        if ($params !== null) {
            for ($i = 0; $i < count($params); $i++) {
                if (self::TAB_LENGTH_KEY === $params[$i]->getName()) {
                    $this->tabLength = $params[$i]->getValue();
                    break;
                }
            }
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

require_once 'phing/filters/BaseParamFilterReader.php';

/**
 * Reads the last <code>n</code> lines of a stream. (Default is last10 lines.)
 *
 * Example:
 *
 * <pre><tailfilter lines="3" /></pre>
 *
 * Or:
 *
 * <pre><filterreader classname="phing.filters.TailFilter">
 *   <param name="lines" value="3">
 * </filterreader></pre>
 *
 * @author    <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a>
 * @author    hans lellelid, hans@velum.net
 * @copyright 2003 seasonfive. All rights reserved
 *
 * @see       BaseParamFilterReader
 *
 * @package   phing.filters
 */
class TailFilter extends BaseParamFilterReader implements ChainableReader
{
    /**
     * Parameter name for the number of lines to be returned.
     * @var string
     */
    const LINES_KEY = "lines";

    /**
     * Number of lines to be returned in the filtered stream.
     * @var integer
     */
    private $_lines = 10;

    /**
     * Array to hold lines.
     * @var array
     */
    private $_lineBuffer = array();

    /**
     * Returns the last n lines of a file.
     * @param  int   $len Num chars to read.
     * @return mixed The filtered buffer or -1 if EOF.
     */
    public function read($len = null)
    {

        while (($buffer = $this->in->read($len)) !== -1) {
            // Remove the last "\n" from buffer for
            // prevent explode to add an empty cell at
            // the end of array
            $buffer = trim($buffer, "\n");

            $lines = explode("\n", $buffer);

            if (count($lines) >= $this->_lines) {
                // Buffer have more (or same) number of lines than needed.
                // Fill lineBuffer with the last "$this->_lines" lasts ones.
                $off = count($lines) - $this->_lines;
                $this->_lineBuffer = array_slice($lines, $off);
            } else {
                // Some new lines ...
                // Prepare space for insert these new ones
                $this->_lineBuffer = array_slice($this->_lineBuffer, count($lines) - 1);
                $this->_lineBuffer = array_merge($this->_lineBuffer, $lines);
            }
        }

        if (empty($this->_lineBuffer)) {
            $ret = -1;
        } else {
            $ret = implode("\n", $this->_lineBuffer);
            $this->_lineBuffer = array();
        }

        return $ret;
    }

    /**
     * Sets the number of lines to be returned in the filtered stream.
     *
     * @param integer $lines the number of lines to be returned in the filtered stream.
     */
    public function setLines($lines)
    {
        $this->_lines = (int) $lines;
    }

    /**
     * Returns the number of lines to be returned in the filtered stream.
     *
     * @return integer The number of lines to be returned in the filtered stream.
     */
    public function getLines()
    {
        return $this->_lines;
    }

    /**
     * Creates a new TailFilter using the passed in
     * Reader for instantiation.
     *
     * @param Reader $reader A Reader object providing the underlying stream.
     *                       Must not be <code>null</code>.
     *
     * @return TailFilter A new filter based on this configuration, but filtering
     *                    the specified reader.
     */
    public function chain(Reader $reader)
    {
        $newFilter = new TailFilter($reader);
        $newFilter->setLines($this->getLines());
        $newFilter->setInitialized(true);
        $newFilter->setProject($this->getProject());

        return $newFilter;
    }

    /**
     * Scans the parameters list for the "lines" parameter and uses
     * it to set the number of lines to be returned in the filtered stream.
     */
    private function _initialize()
    {
        $params = $this->getParameters();
        if ($params !== null) {
            for ($i = 0, $_i = count($params); $i < $_i; $i++) {
                if (self::LINES_KEY == $params[$i]->getName()) {
                    $this->_lines = (int) $params[$i]->getValue();
                    break;
                }
            }
        }
    }
}
<?php
/*
 *  $Id: 21f7fd2edf8bbbbe620be3d3944e4cdf00cbc264 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/filters/BaseParamFilterReader.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * This filter uses the bundled-with-PHP Tidy extension to filter input.
 *
 * <p>
 * Example:<br/>
 * <pre>
 * <tidyfilter encoding="utf8">
 *   <config name="indent" value="true"/>
 *   <config name="output-xhtml" value="true"/>
 * </tidyfilter>
 * </pre>
 *
 * @author Hans Lellelid <hans@xmpl.org>
 * @version   $Id: 21f7fd2edf8bbbbe620be3d3944e4cdf00cbc264 $
 * @package   phing.filters
 */
class TidyFilter extends BaseParamFilterReader implements ChainableReader
{

    /** @var string Encoding of resulting document. */
    private $encoding = 'utf8';

    /** @var array Parameter[] */
    private $configParameters = array();

    /**
     * Set the encoding for resulting (X)HTML document.
     * @param string $v
     */
    public function setEncoding($v)
    {
        $this->encoding = $v;
    }

    /**
     * Sets the config params.
     * @param array Parameter[]
     * @see chain()
     */
    public function setConfigParameters($params)
    {
        $this->configParameters = $params;
    }

    /**
     * Adds a <config> element (which is a Parameter).
     * @return Parameter
     */
    public function createConfig()
    {
        $num = array_push($this->configParameters, new Parameter());

        return $this->configParameters[$num - 1];
    }

    /**
     * Converts the Parameter objects being used to store configuration into a simle assoc array.
     * @return array
     */
    private function getDistilledConfig()
    {
        $config = array();
        foreach ($this->configParameters as $p) {
            $config[$p->getName()] = $p->getValue();
        }

        return $config;
    }

    /**
     * Reads input and returns Tidy-filtered output.
     *
     * @param null $len
     * @throws BuildException
     * @return the resulting stream, or -1 if the end of the resulting stream has been reached
     *
     */
    public function read($len = null)
    {

        if (!class_exists('Tidy')) {
            throw new BuildException("You must enable the 'tidy' extension in your PHP configuration in order to use the Tidy filter.");
        }

        if (!$this->getInitialized()) {
            $this->_initialize();
            $this->setInitialized(true);
        }

        $buffer = $this->in->read($len);
        if ($buffer === -1) {
            return -1;
        }

        $config = $this->getDistilledConfig();

        $tidy = new Tidy();
        $tidy->parseString($buffer, $config, $this->encoding);
        $tidy->cleanRepair();

        return tidy_get_output($tidy);

    }

    /**
     * Creates a new TidyFilter using the passed in Reader for instantiation.
     *
     * @param A|Reader $reader
     * @internal param A $reader Reader object providing the underlying stream.
     *               Must not be <code>null</code>.
     *
     * @return a new filter based on this configuration, but filtering
     *           the specified reader
     */
    public function chain(Reader $reader)
    {
        $newFilter = new TidyFilter($reader);
        $newFilter->setConfigParameters($this->configParameters);
        $newFilter->setEncoding($this->encoding);
        $newFilter->setProject($this->getProject());

        return $newFilter;
    }

    /**
     * Initializes any parameters (e.g. config options).
     * This method is only called when this filter is used through a <filterreader> tag in build file.
     */
    private function _initialize()
    {
        $params = $this->getParameters();
        if ($params) {
            foreach ($params as $param) {
                if ($param->getType() == "config") {
                    $this->configParameters[] = $param;
                } else {

                    if ($param->getName() == "encoding") {
                        $this->setEncoding($param->getValue());
                    }

                }

            }
        }
    }

}
<?php

/*
 *  $Id: ceb6f10071ec525cd89b9f6dba2564971aa67a72 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

require_once 'phing/filters/BaseParamFilterReader.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * Replaces gettext("message id") and _("message id") with the translated string.
 *
 * Gettext is great for creating multi-lingual sites, but in some cases (e.g. for
 * performance reasons) you may wish to replace the gettext calls with the translations
 * of the strings; that's what this task is for.  Note that this is similar to
 * ReplaceTokens, but both the find and the replace aspect is more complicated -- hence
 * this is a separate, stand-alone filter.
 *
 * <p>
 * Example:<br>
 * <pre>
 * <translategettext locale="en_US" domain="messages" dir="${webroot}/local"/>
 * </pre>
 *
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: ceb6f10071ec525cd89b9f6dba2564971aa67a72 $
 * @see       BaseFilterReader
 * @package   phing.filters
 */
class TranslateGettext extends BaseParamFilterReader implements ChainableReader
{

    // constants for specifying keys to expect
    // when this is called using <filterreader ... />
    const DOMAIN_KEY = "domain";
    const DIR_KEY = "dir";
    const LOCALE_KEY = "locale";

    /** The domain to use */
    private $domain = 'messages';

    /** The dir containing LC_MESSAGES */
    private $dir;

    /** The locale to use */
    private $locale;

    /** The system locale before it was changed for this filter. */
    private $storedLocale;

    /**
     * Set the text domain to use.
     * The text domain must correspond to the name of the compiled .mo files.
     * E.g. "messages" ==> $dir/LC_MESSAGES/messages.mo
     *         "mydomain" ==> $dir/LC_MESSAGES/mydomain.mo
     * @param string $domain
     */
    public function setDomain($domain)
    {
        $this->domain = $domain;
    }

    /**
     * Get the current domain.
     * @return string
     */
    public function getDomain()
    {
        return $this->domain;
    }

    /**
     * Sets the root locale directory.
     * @param PhingFile $dir
     */
    public function setDir(PhingFile $dir)
    {
        $this->dir = $dir;
    }

    /**
     * Gets the root locale directory.
     * @return PhingFile
     */
    public function getDir()
    {
        return $this->dir;
    }

    /**
     * Sets the locale to use for translation.
     * Note that for gettext() to work, you have to make sure this locale
     * is specific enough for your system (e.g. some systems may allow an 'en' locale,
     * but others will require 'en_US', etc.).
     * @param string $locale
     */
    public function setLocale($locale)
    {
        $this->locale = $locale;
    }

    /**
     * Gets the locale to use for translation.
     * @return string
     */
    public function getLocale()
    {
        return $this->locale;
    }

    /**
     * Make sure that required attributes are set.
     * @throws BuldException - if any required attribs aren't set.
     */
    protected function checkAttributes()
    {
        if (!$this->domain || !$this->locale || !$this->dir) {
            throw new BuildException("You must specify values for domain, locale, and dir attributes.");
        }
    }

    /**
     * Initialize the gettext/locale environment.
     * This method will change some env vars and locale settings; the
     * restoreEnvironment should put them all back :)
     *
     * @return void
     * @throws BuildException - if locale cannot be set.
     * @see restoreEnvironment()
     */
    protected function initEnvironment()
    {
        $this->storedLocale = getenv("LANG");

        $this->log("Setting locale to " . $this->locale, Project::MSG_DEBUG);
        putenv("LANG=" . $this->locale);
        $ret = setlocale(LC_ALL, $this->locale);
        if ($ret === false) {
            $msg = "Could not set locale to " . $this->locale
                . ". You may need to use fully qualified name"
                . " (e.g. en_US instead of en).";
            throw new BuildException($msg);
        }

        $this->log("Binding domain '" . $this->domain . "' to " . $this->dir, Project::MSG_DEBUG);
        bindtextdomain($this->domain, $this->dir->getAbsolutePath());
        textdomain($this->domain);
    }

    /**
     * Restores environment settings and locale.
     * This does _not_ restore any gettext-specific settings
     * (e.g. textdomain()).
     *
     * @return void
     */
    protected function restoreEnvironment()
    {
        putenv("LANG=" . $this->storedLocale);
        setlocale(LC_ALL, $this->storedLocale);
    }

    /**
     * Performs gettext translation of msgid and returns translated text.
     *
     * This function simply wraps gettext() call, but provides ability to log
     * string replacements.  (alternative would be using preg_replace with /e which
     * would probably be faster, but no ability to debug/log.)
     *
     * @param  array  $matches Array of matches; we're interested in $matches[2].
     * @return string Translated text
     */
    private function xlateStringCallback($matches)
    {
        $charbefore = $matches[1];
        $msgid = $matches[2];
        $translated = gettext($msgid);
        $this->log("Translating \"$msgid\" => \"$translated\"", Project::MSG_DEBUG);

        return $charbefore . '"' . $translated . '"';
    }

    /**
     * Returns the filtered stream.
     * The original stream is first read in fully, and then translation is performed.
     *
     * @param null $len
     * @throws BuildException
     * @return mixed the filtered stream, or -1 if the end of the resulting stream has been reached.
     *
     */
    public function read($len = null)
    {

        if (!$this->getInitialized()) {
            $this->_initialize();
            $this->setInitialized(true);
        }

        // Make sure correct params/attribs have been set
        $this->checkAttributes();

        $buffer = $this->in->read($len);
        if ($buffer === -1) {
            return -1;
        }

        // Setup the locale/gettext environment
        $this->initEnvironment();


        // replace any occurrences of _("") or gettext("") with
        // the translated value.
        //
        // ([^\w]|^)_\("((\\"|[^"])*)"\)
        //  --$1---      -----$2----
        //                 ---$3--  [match escaped quotes or any char that's not a quote]
        //
        // also match gettext() -- same as above

        $buffer = preg_replace_callback(
            '/([^\w]|^)_\("((\\\"|[^"])*)"\)/',
            array($this, 'xlateStringCallback'),
            $buffer
        );
        $buffer = preg_replace_callback(
            '/([^\w]|^)gettext\("((\\\"|[^"])*)"\)/',
            array($this, 'xlateStringCallback'),
            $buffer
        );

        // Check to see if there are any _('') calls and flag an error

        // Check to see if there are any unmatched gettext() calls -- and flag an error

        $matches = array();
        if (preg_match('/([^\w]|^)(gettext\([^\)]+\))/', $buffer, $matches)) {
            $this->log("Unable to perform translation on: " . $matches[2], Project::MSG_WARN);
        }

        $this->restoreEnvironment();

        return $buffer;
    }

    /**
     * Creates a new TranslateGettext filter using the passed in
     * Reader for instantiation.
     *
     * @param Reader $reader A Reader object providing the underlying stream.
     *                       Must not be <code>null</code>.
     *
     * @return TranslateGettext A new filter based on this configuration, but filtering
     *                          the specified reader
     */
    public function chain(Reader $reader)
    {
        $newFilter = new TranslateGettext($reader);
        $newFilter->setProject($this->getProject());
        $newFilter->setDomain($this->getDomain());
        $newFilter->setLocale($this->getLocale());
        $newFilter->setDir($this->getDir());

        return $newFilter;
    }

    /**
     * Parses the parameters if this filter is being used in "generic" mode.
     */
    private function _initialize()
    {
        $params = $this->getParameters();
        if ($params !== null) {
            foreach ($params as $param) {
                switch ($param->getType()) {
                    case self::DOMAIN_KEY:
                        $this->setDomain($param->getValue());
                        break;
                    case self::DIR_KEY:
                        $this->setDir($this->project->resolveFile($param->getValue()));
                        break;

                    case self::LOCALE_KEY:
                        $this->setLocale($param->getValue());
                        break;
                } // switch
            }
        } // if params !== null
    }
}
<?php
/*
 *  $Id: 46536018ea0389c5baca8c49666d35c7524ee15b $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/Project.php';
include_once 'phing/filters/BaseFilterReader.php';
include_once 'phing/types/PhingFilterReader.php';
include_once 'phing/types/FilterChain.php';
include_once 'phing/types/Parameter.php';
include_once 'phing/util/FileUtils.php';
include_once 'phing/util/StringHelper.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * Process a FilterReader chain.
 *
 * Here, the interesting method is 'getAssembledReader'.
 * The purpose of this one is to create a simple Reader object which
 * apply all filters on another primary Reader object.
 *
 * For example : In copyFile (phing.util.FileUtils) the primary Reader
 * is a FileReader object (more accuratly, a BufferedReader) previously
 * setted for the source file to copy. So, consider this filterchain :
 *
 *     <filterchain>
 *        <stripphpcomments />
 *        <linecontains>
 *            <contains value="foo">
 *        </linecontains>
 *      <tabtospaces tablength="8" />
 *    </filterchain>
 *
 *    getAssembledReader will return a Reader object wich read on each
 *    of these filters. Something like this : ('->' = 'which read data from') :
 *
 *  [TABTOSPACES] -> [LINECONTAINS] -> [STRIPPHPCOMMENTS] -> [FILEREADER]
 *                                                         (primary reader)
 *
 *  So, getAssembledReader will return the TABTOSPACES Reader object. Then
 *  each read done with this Reader object will follow this path.
 *
 *    Hope this explanation is clear :)
 *
 * TODO: Implement the classPath feature.
 *
 * @author    <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a>
 * @version   $Id: 46536018ea0389c5baca8c49666d35c7524ee15b $
 * @package   phing.filters.util
 */
class ChainReaderHelper
{

    /** Primary reader to wich the reader chain is to be attached */
    private $primaryReader = null;

    /** The site of the buffer to be used. */
    private $bufferSize = 8192;

    /** Chain of filters */
    private $filterChains = array();

    /** The Phing project */
    private $project;

    /*
     * Sets the primary reader
    */
    /**
     * @param Reader $reader
     */
    public function setPrimaryReader(Reader $reader)
    {
        $this->primaryReader = $reader;
    }

    /*
     * Set the project to work with
    */
    /**
     * @param Project $project
     */
    public function setProject(Project $project)
    {
        $this->project = $project;
    }

    /*
     * Get the project
    */
    public function getProject()
    {
        return $this->project;
    }

    /*
     * Sets the buffer size to be used.  Defaults to 8192,
     * if this method is not invoked.
    */
    /**
     * @param $size
     */
    public function setBufferSize($size)
    {
        $this->bufferSize = $size;
    }

    /*
     * Sets the collection of filter reader sets
    */
    /**
     * @param $fchain
     */
    public function setFilterChains(&$fchain)
    {
        $this->filterChains = & $fchain;
    }

    /*
     * Assemble the reader
    */
    /**
     * @return FilterReader|null|Parameterizable|Reader
     * @throws Exception
     */
    public function getAssembledReader()
    {

        $instream = $this->primaryReader;
        $filterReadersCount = count($this->filterChains);
        $finalFilters = array();

        // Collect all filter readers of all filter chains used ...
        for ($i = 0; $i < $filterReadersCount; $i++) {
            $filterchain = & $this->filterChains[$i];
            $filterReaders = $filterchain->getFilterReaders();
            $readerCount = count($filterReaders);
            for ($j = 0; $j < $readerCount; $j++) {
                $finalFilters[] = $filterReaders[$j];
            }
        }

        // ... then chain the filter readers.
        $filtersCount = count($finalFilters);
        if ($filtersCount > 0) {
            for ($i = 0; $i < $filtersCount; $i++) {
                $filter = $finalFilters[$i];

                if ($filter instanceof PhingFilterReader) {

                    // This filter reader is an external class.
                    $className = $filter->getClassName();
                    $classpath = $filter->getClasspath();
                    $project = $filter->getProject();

                    if ($className !== null) {
                        $cls = Phing::import($className, $classpath);
                        $impl = new $cls();
                    }

                    if (!($impl instanceof FilterReader)) {
                        throw new Exception($className . " does not extend phing.system.io.FilterReader");
                    }

                    $impl->setReader($instream); // chain
                    $impl->setProject($this->getProject()); // what about $project above ?

                    if ($impl instanceof Parameterizable) {
                        $impl->setParameters($filter->getParams());
                    }

                    $instream = $impl; // now that it's been chained

                } elseif (($filter instanceof ChainableReader) && ($filter instanceof Reader)) {
                    if ($this->getProject() !== null && ($filter instanceof BaseFilterReader)) {
                        $filter->setProject($this->getProject());
                    }
                    $instream = $filter->chain($instream);
                } else {
                    throw new Exception("Cannot chain invalid filter: " . get_class($filter));
                }
            }
        }

        return $instream;
    }

}
<?php
/*
 *  $Id: 1787bbe5c2ac3fc35faf21652eed36899bff61b6 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/types/TokenReader.php';
include_once 'phing/system/io/IOException.php';
include_once 'phing/filters/ReplaceTokens.php'; // For class Token

/**
 * Class that allows reading tokens from INI files.
 *
 * @author    Manuel Holtgewe
 * @version   $Id: 1787bbe5c2ac3fc35faf21652eed36899bff61b6 $
 * @package   phing.filters.util
 */
class IniFileTokenReader extends TokenReader
{

    /**
     * Holds the path to the INI file that is to be read.
     * @var object  Reference to a PhingFile Object representing
     *              the path to the INI file.
     */
    private $file = null;

    /**
     * @var string  Sets the section to load from the INI file.
     *              if omitted, all sections are loaded.
     */
    private $section = null;

    /**
     * @var array
     */
    private $tokens = null;

    /**
     * Reads the next token from the INI file
     *
     * @throws BuildException
     * @return Token
     */
    public function readToken()
    {
        if ($this->file === null) {
            throw new BuildException("No File set for IniFileTokenReader");
        }

        if ($this->tokens === null) {
            $this->processFile();
        }

        if (count($this->tokens) > 0) {
            return array_pop($this->tokens);
        } else {
            return null;
        }
    }

    /**
     * Parse & process the ini file
     */
    protected function processFile()
    {
        $arr = parse_ini_file($this->file->getAbsolutePath(), true);

        if ($this->section !== null) {
            if (isset($arr[$this->section])) {
                $this->processSection($arr[$this->section]);
            }

            return;
        }

        $values = array_values($arr);

        if (!is_array($values[0])) {
            $this->processSection($arr);

            return;
        }

        foreach ($values as $subArr) {
            $this->processSection($subArr);
        }
    }

    /**
     * Process an individual section
     *
     * @param array $section
     */
    protected function processSection(array $section)
    {
        foreach ($section as $key => $value) {
            $tok = new Token();
            $tok->setKey($key);
            $tok->setValue($value);
            $this->tokens[] = $tok;
        }
    }

    /**
     * @param string|PhingFile $file
     * @throws BuildException
     */
    public function setFile($file)
    {
        if (is_string($file)) {
            $this->file = new PhingFile($file);

            return;
        }

        if (is_object($file) && $file instanceof PhingFile) {
            $this->file = $file;

            return;
        }

        throw new BuildException("Unsupported value " . (string) $file);
    }

    /**
     * @param $str
     */
    public function setSection($str)
    {
        $this->section = (string) $str;
    }
}
<?php

/*
 *  $Id: a7da72148cfff4f3daf6f4ea7036f56f5ef2d955 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/filters/BaseParamFilterReader.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * Applies Xinclude parsing to incoming text.
 *
 * Uses PHP DOM XML support
 *
 * @author    Bill Karwin <bill@karwin.com>
 * @version   $Id: a7da72148cfff4f3daf6f4ea7036f56f5ef2d955 $
 * @see       FilterReader
 * @package   phing.filters
 */
class XincludeFilter extends BaseParamFilterReader implements ChainableReader
{

    private $basedir = null;

    /**
     * @var bool
     */
    private $processed = false;

    /**
     * Whether to resolve entities.
     *
     * @var bool
     *
     * @since 2.4
     */
    private $resolveExternals = false;

    /**
     * Whether to resolve entities.
     *
     * @param $resolveExternals
     *
     * @since 2.4
     */
    public function setResolveExternals($resolveExternals)
    {
        $this->resolveExternals = (bool) $resolveExternals;
    }

    /**
     * @return bool
     *
     * @since 2.4
     */
    public function getResolveExternals()
    {
        return $this->resolveExternals;
    }

    /**
     * @param PhingFile $dir
     */
    public function setBasedir(PhingFile $dir)
    {
        $this->basedir = $dir;
    }

    /**
     * @return null
     */
    public function getBasedir()
    {
        return $this->basedir;
    }

    /**
     * Reads stream, applies XSLT and returns resulting stream.
     * @param null $len
     * @throws BuildException
     * @return string         transformed buffer.
     */
    public function read($len = null)
    {

        if (!class_exists('DomDocument')) {
            throw new BuildException("Could not find the DomDocument class. Make sure PHP has been compiled/configured to support DOM XML.");
        }

        if ($this->processed === true) {
            return -1; // EOF
        }

        // Read XML
        $_xml = null;
        while (($data = $this->in->read($len)) !== -1) {
            $_xml .= $data;
        }

        if ($_xml === null) { // EOF?

            return -1;
        }

        if (empty($_xml)) {
            $this->log("XML file is empty!", Project::MSG_WARN);

            return '';
        }

        $this->log("Transforming XML " . $this->in->getResource() . " using Xinclude ", Project::MSG_VERBOSE);

        $out = '';
        try {
            $out = $this->process($_xml);
            $this->processed = true;
        } catch (IOException $e) {
            throw new BuildException($e);
        }

        return $out;
    }

    /**
     * Try to process the Xinclude transformation
     *
     * @param   string  XML to process.
     *
     * @return string
     * @throws BuildException On errors
     */
    protected function process($xml)
    {

        if ($this->basedir) {
            $cwd = getcwd();
            chdir($this->basedir);
        }

        // Create and setup document.
        $xmlDom = new DomDocument();
        $xmlDom->resolveExternals = $this->resolveExternals;

        $xmlDom->loadXML($xml);

        $xmlDom->xinclude();

        if ($this->basedir) {
            chdir($cwd);
        }

        return $xmlDom->saveXML();
    }

    /**
     * Creates a new XincludeFilter using the passed in
     * Reader for instantiation.
     *
     * @param Reader A Reader object providing the underlying stream.
     *               Must not be <code>null</code>.
     *
     * @return Reader A new filter based on this configuration, but filtering
     *                the specified reader
     */
    public function chain(Reader $reader)
    {
        $newFilter = new XincludeFilter($reader);
        $newFilter->setProject($this->getProject());
        $newFilter->setBasedir($this->getBasedir());

        return $newFilter;
    }

}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/filters/BaseParamFilterReader.php';
include_once 'phing/filters/ChainableReader.php';

/**
 * Applies XSL stylesheet to incoming text.
 *
 * Uses PHP XSLT support (libxslt).
 *
 * @author    Hans Lellelid <hans@velum.net>
 * @author    Yannick Lecaillez <yl@seasonfive.com>
 * @author    Andreas Aderhold <andi@binarycloud.com>
 *
 * @see       FilterReader
 * @package   phing.filters
 */
class XsltFilter extends BaseParamFilterReader implements ChainableReader
{

    /**
     * Path to XSL stylesheet.
     * @var string
     */
    private $xslFile = null;

    /**
     * Whether XML file has been transformed.
     * @var boolean
     */
    private $processed = false;

    /**
     * XSLT Params.
     * @var array
     */
    private $xsltParams = array();

    /**
     * Whether to use loadHTML() to parse the input XML file.
     */
    private $html = false;

    /**
     * Whether to resolve entities in the XML document (see
     * {@link http://www.php.net/manual/en/class.domdocument.php#domdocument.props.resolveexternals}
     * for more details).
     *
     * @var bool
     *
     * @since 2.4
     */
    private $resolveDocumentExternals = false;

    /**
     * Whether to resolve entities in the stylesheet.
     *
     * @var bool
     *
     * @since 2.4
     */
    private $resolveStylesheetExternals = false;

    /**
     * Create new XSLT Param object, to handle the <param/> nested element.
     * @return XSLTParam
     */
    public function createParam()
    {
        $num = array_push($this->xsltParams, new XSLTParam());

        return $this->xsltParams[$num - 1];
    }

    /**
     * Sets the XSLT params for this class.
     * This is used to "clone" this class, in the chain() method.
     * @param array $params
     */
    public function setParams($params)
    {
        $this->xsltParams = $params;
    }

    /**
     * Returns the XSLT params set for this class.
     * This is used to "clone" this class, in the chain() method.
     * @return array
     */
    public function getParams()
    {
        return $this->xsltParams;
    }

    /**
     * Set the XSLT stylesheet.
     * @param mixed $file PhingFile object or path.
     */
    public function setStyle(PhingFile $file)
    {
        $this->xslFile = $file;
    }

    /**
     * Whether to use HTML parser for the XML.
     * This is supported in libxml2 -- Yay!
     * @return boolean
     */
    public function getHtml()
    {
        return $this->html;
    }

    /**
     * Whether to use HTML parser for XML.
     * @param boolean $b
     */
    public function setHtml($b)
    {
        $this->html = (boolean) $b;
    }

    /**
     * Get the path to XSLT stylesheet.
     * @return mixed XSLT stylesheet path.
     */
    public function getStyle()
    {
        return $this->xslFile;
    }

    /**
     * Whether to resolve entities in document.
     *
     * @param bool $resolveExternals
     *
     * @since 2.4
     */
    public function setResolveDocumentExternals($resolveExternals)
    {
        $this->resolveDocumentExternals = (bool) $resolveExternals;
    }

    /**
     * @return bool
     *
     * @since 2.4
     */
    public function getResolveDocumentExternals()
    {
        return $this->resolveDocumentExternals;
    }

    /**
     * Whether to resolve entities in stylesheet.
     *
     * @param bool $resolveExternals
     *
     * @since 2.4
     */
    public function setResolveStylesheetExternals($resolveExternals)
    {
        $this->resolveStylesheetExternals = (bool) $resolveExternals;
    }

    /**
     * @return bool
     *
     * @since 2.4
     */
    public function getResolveStylesheetExternals()
    {
        return $this->resolveStylesheetExternals;
    }

    /**
     * Reads stream, applies XSLT and returns resulting stream.
     * @param null $len
     * @throws BuildException
     * @return string         transformed buffer.
     */
    public function read($len = null)
    {

        if (!class_exists('XSLTProcessor')) {
            throw new BuildException("Could not find the XSLTProcessor class. Make sure PHP has been compiled/configured to support XSLT.");
        }

        if ($this->processed === true) {
            return -1; // EOF
        }

        if (!$this->getInitialized()) {
            $this->_initialize();
            $this->setInitialized(true);
        }

        // Read XML
        $_xml = null;
        while (($data = $this->in->read($len)) !== -1) {
            $_xml .= $data;
        }

        if ($_xml === null) { // EOF?

            return -1;
        }

        if (empty($_xml)) {
            $this->log("XML file is empty!", Project::MSG_WARN);

            return ''; // return empty string, don't attempt to apply XSLT
        }

        // Read XSLT
        $_xsl = null;
        $xslFr = new FileReader($this->xslFile);
        $xslFr->readInto($_xsl);

        $this->log(
            "Tranforming XML " . $this->in->getResource() . " using style " . $this->xslFile->getPath(),
            Project::MSG_VERBOSE
        );

        $out = '';
        try {
            $out = $this->process($_xml, $_xsl);
            $this->processed = true;
        } catch (IOException $e) {
            throw new BuildException($e);
        }

        return $out;
    }

    // {{{ method _ProcessXsltTransformation($xml, $xslt) throws BuildException
    /**
     * Try to process the XSLT transformation
     *
     * @param string $xml XML to process.
     * @param string $xsl XSLT sheet to use for the processing.
     *
     * @return string
     *
     * @throws BuildException On XSLT errors
     */
    protected function process($xml, $xsl)
    {

        $processor = new XSLTProcessor();

        // Create and setup document.
        $xmlDom = new DOMDocument();
        $xmlDom->resolveExternals = $this->resolveDocumentExternals;

        // Create and setup stylesheet.
        $xslDom = new DOMDocument();
        $xslDom->resolveExternals = $this->resolveStylesheetExternals;

        if ($this->html) {
            $xmlDom->loadHTML($xml);
        } else {
            $xmlDom->loadXML($xml);
        }

        $xslDom->loadxml($xsl);

        if (defined('XSL_SECPREF_WRITE_FILE')) {
            if (version_compare(PHP_VERSION, '5.4', "<")) {
                ini_set("xsl.security_prefs", XSL_SECPREF_WRITE_FILE | XSL_SECPREF_CREATE_DIRECTORY);
            } else {
                $processor->setSecurityPrefs(XSL_SECPREF_WRITE_FILE | XSL_SECPREF_CREATE_DIRECTORY);
            }
        }
        $processor->importStylesheet($xslDom);

        // ignoring param "type" attrib, because
        // we're only supporting direct XSL params right now
        foreach ($this->xsltParams as $param) {
            $this->log("Setting XSLT param: " . $param->getName() . "=>" . $param->getExpression(), Project::MSG_DEBUG);
            $processor->setParameter(null, $param->getName(), $param->getExpression());
        }

        $errorlevel = error_reporting();
        error_reporting($errorlevel & ~E_WARNING);
        @$result = $processor->transformToXML($xmlDom);
        error_reporting($errorlevel);

        if (false === $result) {
            //$errno = xslt_errno($processor);
            //$err   = xslt_error($processor);
            throw new BuildException("XSLT Error");
        } else {
            return $result;
        }
    }

    /**
     * Creates a new XsltFilter using the passed in
     * Reader for instantiation.
     *
     * @param Reader A Reader object providing the underlying stream.
     *               Must not be <code>null</code>.
     *
     * @return XsltFilter A new filter based on this configuration, but filtering
     *                    the specified reader
     */
    public function chain(Reader $reader)
    {
        $newFilter = new XsltFilter($reader);
        $newFilter->setProject($this->getProject());
        $newFilter->setStyle($this->getStyle());
        $newFilter->setInitialized(true);
        $newFilter->setParams($this->getParams());
        $newFilter->setHtml($this->getHtml());

        return $newFilter;
    }

    /**
     * Parses the parameters to get stylesheet path.
     */
    private function _initialize()
    {
        $params = $this->getParameters();
        if ($params !== null) {
            for ($i = 0, $_i = count($params); $i < $_i; $i++) {
                if ($params[$i]->getType() === null) {
                    if ($params[$i]->getName() === "style") {
                        $this->setStyle($params[$i]->getValue());
                    }
                } elseif ($params[$i]->getType() == "param") {
                    $xp = new XSLTParam();
                    $xp->setName($params[$i]->getName());
                    $xp->setExpression($params[$i]->getValue());
                    $this->xsltParams[] = $xp;
                }
            }
        }
    }
}

/**
 * Class that holds an XSLT parameter.
 *
 * @package   phing.filters
 */
class XSLTParam
{

    private $name;

    /** @var RegisterSlot */
    private $expr;

    /**
     * Sets param name.
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Get param name.
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Sets expression value (alias to the setExpression()) method.
     *
     * @param string $v
     * @see setExpression()
     */
    public function setValue($v)
    {
        $this->setExpression($v);
    }

    /**
     * Gets expression value (alias to the getExpression()) method.
     *
     * @return string
     * @see getExpression()
     */
    public function getValue()
    {
        return $this->getExpression();
    }

    /**
     * Sets expression value.
     * @param string $expr
     */
    public function setExpression($expr)
    {
        $this->expr = $expr;
    }

    /**
     * Sets expression to dynamic register slot.
     * @param RegisterSlot $expr
     */
    public function setListeningExpression(RegisterSlot $expr)
    {
        $this->expr = $expr;
    }

    /**
     * Returns expression value -- performs lookup if expr is registerslot.
     *
     * @return string
     */
    public function getExpression()
    {
        if ($this->expr instanceof RegisterSlot) {
            return $this->expr->getValue();
        } else {
            return $this->expr;
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/input/InputHandler.php';
include_once 'phing/system/io/ConsoleReader.php';

/**
 * Prompts using print(); reads input from Console.
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Stefan Bodewig <stefan.bodewig@epost.de> (Ant)
 *
 * @package phing.input
 */
class DefaultInputHandler implements InputHandler
{
    /**
     * Prompts and requests input.
     * May loop until a valid input has been entered.
     *
     * @param InputRequest $request
     *
     * @return void
     *
     * @throws BuildException
     */
    public function handleInput(InputRequest $request)
    {
        $prompt = $this->getPrompt($request);
        $in = new ConsoleReader();
        do {
            print $prompt;
            try {
                $input = $in->readLine();
                if ($input === "" && ($request->getDefaultValue() !== null)) {
                    $input = $request->getDefaultValue();
                }
                $request->setInput($input);
            } catch (Exception $e) {
                throw new BuildException("Failed to read input from Console.", $e);
            }
        } while (!$request->isInputValid());
    }

    /**
     * Constructs user prompt from a request.
     *
     * <p>This implementation adds (choice1,choice2,choice3,...) to the
     * prompt for <code>MultipleChoiceInputRequest</code>s.</p>
     *
     * @param InputRequest $request the request to construct the prompt for.
     *                              Must not be <code>null</code>.
     *
     * @return string
     */
    protected function getPrompt(InputRequest $request)
    {
        $prompt = $request->getPrompt();
        $defaultValue = $request->getDefaultValue();

        if ($request instanceof YesNoInputRequest) {
            $choices = $request->getChoices();
            $defaultValue = $choices[(int) !$request->getDefaultValue()];
            $prompt .= '(' . implode('/', $request->getChoices()) . ')';
        } elseif ($request instanceof MultipleChoiceInputRequest) { // (a,b,c,d)
            $prompt .= '(' . implode(',', $request->getChoices()) . ')';
        }

        if ($request->getDefaultValue() !== null) {
            $prompt .= ' [' . $defaultValue . ']';
        }
        $pchar = $request->getPromptChar();

        return $prompt . ($pchar ? $pchar . ' ' : ' ');
    }
}
<?php

/*
 *  $Id: 5f69c44b818429e2fc92b8cb00e8971fcd6dce67 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Plugin to Phing to handle requests for user input.
 *
 * @author Stefan Bodewig <stefan.bodewig@epost.de>
 * @version $Id: 5f69c44b818429e2fc92b8cb00e8971fcd6dce67 $
 * @package phing.input
 */
interface InputHandler
{

    /**
     * Handle the request encapsulated in the argument.
     *
     * <p>Precondition: the request.getPrompt will return a non-null
     * value.</p>
     *
     * <p>Postcondition: request.getInput will return a non-null
     * value, request.isInputValid will return true.</p>
     * @param InputRequest $request
     * @return void
     */
    public function handleInput(InputRequest $request);

}
<?php

/*
 *  $Id: ff401fe7e6280742731cece9d15924c13dad8b1e $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Encapsulates an input request.
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Stefan Bodewig <stefan.bodewig@epost.de> (Ant)
 * @version $Id: ff401fe7e6280742731cece9d15924c13dad8b1e $
 * @package phing.input
 */
class InputRequest
{

    protected $prompt;
    protected $input;
    protected $defaultValue;
    protected $promptChar;

    /**
     * @param string $prompt The prompt to show to the user.  Must not be null.
     * @throws BuildException
     */
    public function __construct($prompt)
    {
        if ($prompt === null) {
            throw new BuildException("prompt must not be null");
        }
        $this->prompt = $prompt;
    }

    /**
     * Retrieves the prompt text.
     */
    public function getPrompt()
    {
        return $this->prompt;
    }

    /**
     * Sets the user provided input.
     * @param $input
     */
    public function setInput($input)
    {
        $this->input = $input;
    }

    /**
     * Is the user input valid?
     */
    public function isInputValid()
    {
        return true;
    }

    /**
     * Retrieves the user input.
     */
    public function getInput()
    {
        return $this->input;
    }

    /**
     * Set the default value to use.
     * @param mixed $v
     */
    public function setDefaultValue($v)
    {
        $this->defaultValue = $v;
    }

    /**
     * Return the default value to use.
     * @return mixed
     */
    public function getDefaultValue()
    {
        return $this->defaultValue;
    }

    /**
     * Set the default value to use.
     * @param string $c
     */
    public function setPromptChar($c)
    {
        $this->promptChar = $c;
    }

    /**
     * Return the default value to use.
     * @return string
     */
    public function getPromptChar()
    {
        return $this->promptChar;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/input/InputRequest.php';

/**
 * Encapsulates an input request.
 *
 * @author Stefan Bodewig <stefan.bodewig@epost.de>
 *
 * @package phing.input
 */
class MultipleChoiceInputRequest extends InputRequest
{
    /** @var array $choises */
    protected $choices = array();

    /**
     * @param string $prompt  The prompt to show to the user.  Must not be null.
     * @param array  $choices holds all input values that are allowed.
     *                        Must not be null.
     */
    public function __construct($prompt, $choices)
    {
        parent::__construct($prompt);
        $this->choices = $choices;
    }

    /**
     * @return array The possible values.
     */
    public function getChoices()
    {
        return $this->choices;
    }

    /**
     * @return bool true if the input is one of the allowed values.
     */
    public function isInputValid()
    {
        return in_array($this->getInput(), $this->choices); // not strict (?)
    }
}
<?php
/*
 *  $Id: 8f108ef006e6cc3be1e0449384ff96e53b8e2e39 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/input/MultipleChoiceInputRequest.php';

/**
 * Encapsulates an input request that returns a boolean (yes/no).
 *
 * @author Hans Lellelid <hans@xmpl.org>
 * @version $Id: 8f108ef006e6cc3be1e0449384ff96e53b8e2e39 $
 * @package phing.input
 */
class YesNoInputRequest extends MultipleChoiceInputRequest
{

    /**
     * @return true if the input is one of the allowed values.
     */
    public function isInputValid()
    {
        return StringHelper::isBoolean($this->input);
    }

    /**
     * Converts input to boolean.
     * @return boolean
     */
    public function getInput()
    {
        return StringHelper::booleanValue($this->input);
    }
}
<?php

/*
 *  $Id: af7509719aa6a6d1276e8640587764ccd9e5baf2 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/types/Reference.php';
include_once 'phing/types/Path.php';
include_once 'phing/util/StringHelper.php';
include_once 'phing/parser/CustomChildCreator.php';

/**
 * Helper class that collects the methods that a task or nested element
 * holds to set attributes, create nested elements or hold PCDATA
 * elements.
 *
 *<ul>
 * <li><strong>SMART-UP INLINE DOCS</strong></li>
 * <li><strong>POLISH-UP THIS CLASS</strong></li>
 *</ul>
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @author    Hans Lellelid <hans@xmpl.org>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: af7509719aa6a6d1276e8640587764ccd9e5baf2 $
 * @package   phing
 */
class IntrospectionHelper
{

    /**
     * Holds the attribute setter methods.
     *
     * @var array string[]
     */
    private $attributeSetters = array();

    /**
     * Holds methods to create nested elements.
     *
     * @var array string[]
     */
    private $nestedCreators = array();

    /**
     * Holds methods to store configured nested elements.
     *
     * @var array string[]
     */
    private $nestedStorers = array();

    /**
     * Map from attribute names to nested types.
     */
    private $nestedTypes = array();

    /**
     * New idea in phing: any class can register certain
     * keys -- e.g. "task.current_file" -- which can be used in
     * task attributes, if supported.  In the build XML these
     * are referred to like this:
     *         <regexp pattern="\n" replace="%{task.current_file}"/>
     * In the type/task a listener method must be defined:
     *         function setListeningReplace($slot) {}
     * @var array string[]
     */
    private $slotListeners = array();

    /**
     * The method to add PCDATA stuff.
     *
     * @var string Method name of the addText (redundant?) method, if class supports it :)
     */
    private $methodAddText = null;

    /**
     * The Class that's been introspected.
     *
     * @var     object
     */
    private $bean;

    /**
     * The cache of IntrospectionHelper classes instantiated by getHelper().
     * @var array IntrospectionHelpers[]
     */
    private static $helpers = array();

    /**
     * Factory method for helper objects.
     *
     * @param string $class The class to create a Helper for
     */
    public static function getHelper($class)
    {
        if (!isset(self::$helpers[$class])) {
            self::$helpers[$class] = new IntrospectionHelper($class);
        }

        return self::$helpers[$class];
    }

    /**
     * This function constructs a new introspection helper for a specific class.
     *
     * This method loads all methods for the specified class and categorizes them
     * as setters, creators, slot listeners, etc.  This way, the setAttribue() doesn't
     * need to perform any introspection -- either the requested attribute setter/creator
     * exists or it does not & a BuildException is thrown.
     *
     * @param string $class The classname for this IH.
     * @throws BuildException
     */
    public function __construct($class)
    {

        $this->bean = new ReflectionClass($class);

        //$methods = get_class_methods($bean);
        foreach ($this->bean->getMethods() as $method) {

            if ($method->isPublic()) {

                // We're going to keep case-insensitive method names
                // for as long as we're allowed :)  It makes it much
                // easier to map XML attributes to PHP class method names.
                $name = strtolower($method->getName());

                // There are a few "reserved" names that might look like attribute setters
                // but should actually just be skipped.  (Note: this means you can't ever
                // have an attribute named "location" or "tasktype" or a nested element named "task".)
                if ($name === "setlocation" || $name === "settasktype" || $name === "addtask") {
                    continue;
                }

                if ($name === "addtext") {

                    $this->methodAddText = $method;

                } elseif (strpos($name, "setlistening") === 0) {

                    // Phing supports something unique called "RegisterSlots"
                    // These are dynamic values that use a basic slot system so that
                    // classes can register to listen to specific slots, and the value
                    // will always be grabbed from the slot (and never set in the project
                    // component).  This is useful for things like tracking the current
                    // file being processed by a filter (e.g. AppendTask sets an append.current_file
                    // slot, which can be ready by the XSLTParam type.)

                    if (count($method->getParameters()) !== 1) {
                        throw new BuildException($method->getDeclaringClass()->getName() . "::" . $method->getName(
                            ) . "() must take exactly one parameter.");
                    }

                    $this->slotListeners[$name] = $method;

                } elseif (strpos($name, "set") === 0) {

                    // A standard attribute setter.

                    if (count($method->getParameters()) !== 1) {
                        throw new BuildException($method->getDeclaringClass()->getName() . "::" . $method->getName(
                            ) . "() must take exactly one parameter.");
                    }

                    $this->attributeSetters[$name] = $method;

                } elseif (strpos($name, "create") === 0) {

                    if ($method->getNumberOfRequiredParameters() > 0) {
                        throw new BuildException($method->getDeclaringClass()->getName() . "::" . $method->getName(
                            ) . "() may not take any parameters.");
                    }

                    // Because PHP doesn't support return types, we are going to do
                    // two things here to guess return type:
                    //     1) parse comments for an explicit value
                    //     2) if that fails, assume that the part of the method after "create"
                    //    is the name of the return type (in many cases it is not)

                    // This isn't super important -- i.e. we're not instantaiting classes
                    // based on this information.  It's more just so that IntrospectionHelper
                    // can keep track of all the nested types -- and provide more helpful
                    // exception messages, etc.

                    preg_match('/@return[\s]+([\w]+)/', $method->getDocComment(), $matches);
                    if (!empty($matches[1]) && class_exists($matches[1], false)) {
                        $this->nestedTypes[$name] = $matches[1];
                    } else {
                        // assume that method createEquals() creates object of type "Equals"
                        // (that example would be false, of course)
                        $this->nestedTypes[$name] = $this->getPropertyName($name, "create");
                    }

                    $this->nestedCreators[$name] = $method;

                } elseif (strpos($name, "addconfigured") === 0) {

                    // *must* use class hints if using addConfigured ...

                    // 1 param only
                    $params = $method->getParameters();

                    if (count($params) < 1) {
                        throw new BuildException($method->getDeclaringClass()->getName() . "::" . $method->getName(
                            ) . "() must take at least one parameter.");
                    }

                    if (count($params) > 1) {
                        $this->warn(
                            $method->getDeclaringClass()->getName() . "::" . $method->getName(
                            ) . "() takes more than one parameter. (IH only uses the first)"
                        );
                    }

                    $classname = null;

                    if (($hint = $params[0]->getClass()) !== null) {
                        $classname = $hint->getName();
                    }

                    if ($classname === null) {
                        throw new BuildException($method->getDeclaringClass()->getName() . "::" . $method->getName(
                            ) . "() method MUST use a class hint to indicate the class type of parameter.");
                    }

                    $this->nestedTypes[$name] = $classname;

                    $this->nestedStorers[$name] = $method;

                } elseif (strpos($name, "add") === 0) {

                    // *must* use class hints if using add ...

                    // 1 param only
                    $params = $method->getParameters();
                    if (count($params) < 1) {
                        throw new BuildException($method->getDeclaringClass()->getName() . "::" . $method->getName(
                            ) . "() must take at least one parameter.");
                    }

                    if (count($params) > 1) {
                        $this->warn(
                            $method->getDeclaringClass()->getName() . "::" . $method->getName(
                            ) . "() takes more than one parameter. (IH only uses the first)"
                        );
                    }

                    $classname = null;

                    if (($hint = $params[0]->getClass()) !== null) {
                        $classname = $hint->getName();
                    }

                    // we don't use the classname here, but we need to make sure it exists before
                    // we later try to instantiate a non-existent class
                    if ($classname === null) {
                        throw new BuildException($method->getDeclaringClass()->getName() . "::" . $method->getName(
                            ) . "() method MUST use a class hint to indicate the class type of parameter.");
                    }

                    $this->nestedCreators[$name] = $method;
                }
            } // if $method->isPublic()
        } // foreach
    }

    /**
     * Sets the named attribute.
     * @param Project $project
     * @param string $element
     * @param string $attributeName
     * @param mixed $value
     * @throws BuildException
     */
    public function setAttribute(Project $project, $element, $attributeName, &$value)
    {

        // we want to check whether the value we are setting looks like
        // a slot-listener variable:  %{task.current_file}
        //
        // slot-listener variables are not like properties, in that they cannot be mixed with
        // other text values.  The reason for this disparity is that properties are only
        // set when first constructing objects from XML, whereas slot-listeners are always dynamic.
        //
        // This is made possible by PHP5 (objects automatically passed by reference) and PHP's loose
        // typing.

        if (StringHelper::isSlotVar($value)) {

            $as = "setlistening" . strtolower($attributeName);

            if (!isset($this->slotListeners[$as])) {
                $msg = $this->getElementName(
                        $project,
                        $element
                    ) . " doesn't support a slot-listening '$attributeName' attribute.";
                throw new BuildException($msg);
            }

            $method = $this->slotListeners[$as];

            $key = StringHelper::slotVar($value);
            $value = Register::getSlot(
                $key
            ); // returns a RegisterSlot object which will hold current value of that register (accessible using getValue())

        } else {

            // Traditional value options

            $as = "set" . strtolower($attributeName);

            if (!isset($this->attributeSetters[$as])) {
                $msg = $this->getElementName($project, $element) . " doesn't support the '$attributeName' attribute.";
                throw new BuildException($msg);
            }

            $method = $this->attributeSetters[$as];

            if ($as == "setrefid") {
                $value = new Reference($value);
            } else {
                // value is a string representation of a boolean type,
                // convert it to primitive
                if (StringHelper::isBoolean($value)) {

                    $value = StringHelper::booleanValue($value);
                }

                // does method expect a PhingFile object? if so, then
                // pass a project-relative file.
                $params = $method->getParameters();

                $classname = null;

                if (($hint = $params[0]->getClass()) !== null) {
                    $classname = $hint->getName();
                }

                // there should only be one param; we'll just assume ....
                if ($classname !== null) {
                    switch (strtolower($classname)) {
                        case "phingfile":
                            $value = $project->resolveFile($value);
                            break;
                        case "path":
                            $value = new Path($project, $value);
                            break;
                        case "reference":
                            $value = new Reference($value);
                            break;
                        // any other object params we want to support should go here ...
                    }

                } // if hint !== null

            } // if not setrefid

        } // if is slot-listener

        try {
            $project->log(
                "    -calling setter " . $method->getDeclaringClass()->getName() . "::" . $method->getName() . "()",
                Project::MSG_DEBUG
            );
            $method->invoke($element, $value);
        } catch (Exception $exc) {
            throw new BuildException($exc);
        }

    }

    /**
     * Adds PCDATA areas.
     *
     * @param Project $project
     * @param string $element
     * @param string $text
     * @throws BuildException
     */
    public function addText(Project $project, $element, $text)
    {
        if ($this->methodAddText === null) {
            $msg = $this->getElementName($project, $element) . " doesn't support nested text data.";
            throw new BuildException($msg);
        }
        try {
            $method = $this->methodAddText;
            $method->invoke($element, $text);
        } catch (Exception $exc) {
            throw new BuildException($exc);
        }
    }

    /**
     * Creates a named nested element.
     *
     * Valid creators can be in the form createFoo() or addFoo(Bar).
     *
     * @param  Project        $project
     * @param  object         $element     Object the XML tag is child of.
     *                                     Often a task object.
     * @param  string         $elementName XML tag name
     * @return object         Returns the nested element.
     * @throws BuildException
     */
    public function createElement(Project $project, $element, $elementName)
    {

        $addMethod = "add" . strtolower($elementName);
        $createMethod = "create" . strtolower($elementName);
        $nestedElement = null;

        if (isset($this->nestedCreators[$createMethod])) {

            $method = $this->nestedCreators[$createMethod];
            try { // try to invoke the creator method on object
                $project->log(
                    "    -calling creator " . $method->getDeclaringClass()->getName() . "::" . $method->getName(
                    ) . "()",
                    Project::MSG_DEBUG
                );
                $nestedElement = $method->invoke($element);
            } catch (Exception $exc) {
                throw new BuildException($exc);
            }

        } elseif (isset($this->nestedCreators[$addMethod])) {

            $method = $this->nestedCreators[$addMethod];

            // project components must use class hints to support the add methods

            try { // try to invoke the adder method on object

                $project->log(
                    "    -calling adder " . $method->getDeclaringClass()->getName() . "::" . $method->getName() . "()",
                    Project::MSG_DEBUG
                );
                // we've already assured that correct num of params
                // exist and that method is using class hints
                $params = $method->getParameters();

                $classname = null;

                if (($hint = $params[0]->getClass()) !== null) {
                    $classname = $hint->getName();
                }

                // create a new instance of the object and add it via $addMethod
                $clazz = new ReflectionClass($classname);
                if ($clazz->getConstructor() !== null && $clazz->getConstructor()->getNumberOfRequiredParameters() === 1) {
                    $nestedElement = new $classname(Phing::getCurrentProject());
                } else {
                    $nestedElement = new $classname();
                }

                if ($nestedElement instanceof Task && $element instanceof Task) {
                    $nestedElement->setOwningTarget($element->getOwningTarget());
                }

                $method->invoke($element, $nestedElement);

            } catch (Exception $exc) {
                throw new BuildException($exc);
            }
        } elseif ($this->bean->implementsInterface("CustomChildCreator")) {
            $method = $this->bean->getMethod('customChildCreator');

            try {
                $nestedElement = $method->invoke($element, strtolower($elementName), $project);
            } catch (Exception $exc) {
                throw new BuildException($exc);
            }
        } else {
            //try the add method for the element's parent class
            $typedefs = $project->getDataTypeDefinitions();
            if (isset($typedefs[$elementName])) {
                $elementClass = Phing::import($typedefs[$elementName]);
                $parentClass = get_parent_class($elementClass);
                $addMethod = 'add' . strtolower($parentClass);

                if (isset($this->nestedCreators[$addMethod])) {
                    $method = $this->nestedCreators[$addMethod];
                    try {
                        $project->log(
                            "    -calling parent adder "
                            . $method->getDeclaringClass()->getName() . "::" . $method->getName() . "()",
                            Project::MSG_DEBUG
                        );
                        $nestedElement = new $elementClass();
                        $method->invoke($element, $nestedElement);
                    } catch (Exception $exc) {
                        throw new BuildException($exc);
                    }
                }
            }
            if ($nestedElement === null) {
                $msg = $this->getElementName($project, $element) . " doesn't support the '$elementName' creator/adder.";
                throw new BuildException($msg);
            }
        }

        if ($nestedElement instanceof ProjectComponent) {
            $nestedElement->setProject($project);
        }

        return $nestedElement;
    }

    /**
     * Creates a named nested element.
     *
     * @param  Project        $project
     * @param  string         $element
     * @param  string         $child
     * @param  string|null    $elementName
     * @return void
     * @throws BuildException
     */
    public function storeElement($project, $element, $child, $elementName = null)
    {

        if ($elementName === null) {
            return;
        }

        $storer = "addconfigured" . strtolower($elementName);

        if (isset($this->nestedStorers[$storer])) {

            $method = $this->nestedStorers[$storer];

            try {
                $project->log(
                    "    -calling storer " . $method->getDeclaringClass()->getName() . "::" . $method->getName() . "()",
                    Project::MSG_DEBUG
                );
                $method->invoke($element, $child);
            } catch (Exception $exc) {
                throw new BuildException($exc);
            }
        }

    }

    /**
     * Does the introspected class support PCDATA?
     * @return boolean
     */
    public function supportsCharacters()
    {
        return ($this->methodAddText !== null);
    }

    /**
     * Return all attribues supported by the introspected class.
     * @return string[]
     */
    public function getAttributes()
    {
        $attribs = array();
        foreach (array_keys($this->attributeSetters) as $setter) {
            $attribs[] = $this->getPropertyName($setter, "set");
        }

        return $attribs;
    }

    /**
     * Return all nested elements supported by the introspected class.
     * @return string[]
     */
    public function getNestedElements()
    {
        return $this->nestedTypes;
    }

    /**
     * Get the name for an element.
     * When possible the full classnam (phing.tasks.system.PropertyTask) will
     * be returned.  If not available (loaded in taskdefs or typedefs) then the
     * XML element name will be returned.
     *
     * @param  Project $project
     * @param  object  $element The Task or type element.
     * @return string  Fully qualified class name of element when possible.
     */
    public function getElementName(Project $project, $element)
    {

        $taskdefs = $project->getTaskDefinitions();
        $typedefs = $project->getDataTypeDefinitions();

        // check if class of element is registered with project (tasks & types)
        // most element types don't have a getTag() method
        $elClass = get_class($element);

        if (!in_array('getTag', get_class_methods($elClass))) {
            // loop through taskdefs and typesdefs and see if the class name
            // matches (case-insensitive) any of the classes in there
            foreach (array_merge($taskdefs, $typedefs) as $elName => $class) {
                if (0 === strcasecmp($elClass, StringHelper::unqualify($class))) {
                    return $class;
                }
            }

            return "$elClass (unknown)";
        } else {
            // ->getTag() method does exist, so use it
            $elName = $element->getTag();
            if (isset($taskdefs[$elName])) {
                return $taskdefs[$elName];
            } elseif (isset($typedefs[$elName])) {
                return $typedefs[$elName];
            } else {
                return "$elName (unknown)";
            }
        }
    }

    /**
     * Extract the name of a property from a method name - subtracting  a given prefix.
     *
     * @param  string $methodName
     * @param  string $prefix
     * @return string
     */
    public function getPropertyName($methodName, $prefix)
    {
        $start = strlen($prefix);

        return strtolower(substr($methodName, $start));
    }

    /**
     * Prints warning message to screen if -debug was used.
     * @param string $msg
     */
    public function warn($msg)
    {
        if (Phing::getMsgOutputLevel() === Project::MSG_DEBUG) {
            print("[IntrospectionHelper] " . $msg . "\n");
        }
    }

}
<?php

/**
 * Capsule is a simple "template" engine that essentially provides an isolated context
 * for PHP scripts.
 *
 * There is no special templating language, and therefore no limitations to what
 * can be accomplished within templates. The main purpose of Capsule is to separate
 * the business logic from display / output logic.
 *
 * @author Hans Lellelid <hans@xmpl.org>
 *
 * @package phing.lib
 */
class Capsule
{
    /**
     * Look for templates here (if relative path provided).
     * @var string
     */
    protected $templatePath;

    /**
     * Where should output files be written?
     * (This is named inconsistently to be compatible w/ Texen.)
     * @var string
     */
    protected $outputDirectory;

    /**
     * The variables that can be used by the templates.
     * @var array Hash of variables.
     */
    public $vars = array();

    /**
     * Has template been initialized.
     *
     * @var bool
     */
    protected $initialized = false;

    /**
     * Stores the pre-parse() include_path.
     * @var string
     */
    private $old_include_path;

    /**
     *
     */
    public function __construct()
    {
    }

    /**
     * Clears one or several or all variables.
     * @param  mixed $which String name of var, or array of names.
     * @return void
     */
    public function clear($which = null)
    {
        if ($which === null) {
            $this->vars = array();
        } elseif (is_array($which)) {
            foreach ($which as $var) {
                unset($this->vars[$var]);
            }
        } else {
            unset($this->vars[$which]);
        }
    }

    /**
     * Set the basepath to use for template lookups.
     * @param string $v
     */
    public function setTemplatePath($v)
    {
        $this->templatePath = rtrim($v, DIRECTORY_SEPARATOR . '/');
    }

    /**
     * Get the basepath to use for template lookups.
     * @return string
     */
    public function getTemplatePath()
    {
        return $this->templatePath;
    }

    /**
     * Set a basepath to use for output file creation.
     * @param string $v
     */
    public function setOutputDirectory($v)
    {
        $this->outputDirectory = rtrim($v, DIRECTORY_SEPARATOR . '/');
    }

    /**
     * Get basepath to use for output file creation.
     * @return string
     */
    public function getOutputDirectory()
    {
        return $this->outputDirectory;
    }

    /**
     * Low overhead (no output buffering) method to simply dump template
     * to buffer.
     *
     * @param  string    $__template
     * @return void
     * @throws Exception - if template cannot be found
     */
    public function display($__template)
    {

        // Prepend "private" variable names with $__ in this function
        // to keep namespace conflict potential to a minimum.

        // Alias this class to $generator.
        $generator = $this;

        if (isset($this->vars['this'])) {
            throw new Exception("Assigning a variable named \$this to a context conflicts with class namespace.");
        }

        // extract variables into local namespace
        extract($this->vars);

        // prepend template path to include path,
        // so that include "path/relative/to/templates"; can be used within templates
        $__old_inc_path = ini_get('include_path');
        ini_set('include_path', $this->templatePath . PATH_SEPARATOR . $__old_inc_path);

        @ini_set('track_errors', true);
        include $__template;
        @ini_restore('track_errors');

        // restore the include path
        ini_set('include_path', $__old_inc_path);

        if (!empty($php_errormsg)) {
            throw new Exception("Unable to parse template " . $__template . ": " . $php_errormsg);
        }
    }

    /**
     * Fetches the results of a template parse and either returns
     * the string or writes results to a specified output file.
     *
     * @param  string    $template   The template filename (relative to templatePath or absolute).
     * @param  string    $outputFile If specified, contents of template will also be written to this file.
     * @param  boolean   $append     Should output be appended to source file?
     * @return string    The "parsed" template output.
     * @throws Exception - if template not found.
     */
    public function parse($template, $outputFile = null, $append = false)
    {

        // main work done right here:
        // hopefully this works recursively ... fingers crossed.
        ob_start();

        try {
            $this->display($template);
        } catch (Exception $e) {
            ob_end_flush(); // flush the output on error (so we can see up to what point it parsed everything)
            throw $e;
        }

        $output = ob_get_contents();
        ob_end_clean();

        if ($outputFile !== null) {
            $outputFile = $this->resolvePath($outputFile, $this->outputDirectory);

            $flags = null;
            if ($append) {
                $flags = FILE_APPEND;
            }

            if (!file_put_contents($outputFile, $output, $flags) && $output != "") {
                throw new Exception("Unable to write output to " . $outputFile);
            }
        }

        return $output;
    }

    /**
     * This returns a "best guess" path for the given file.
     *
     * @param  string $file     File name or possibly absolute path.
     * @param  string $basepath The basepath that should be prepended if $file is not absolute.
     * @return string "Best guess" path for this file.
     */
    protected function resolvePath($file, $basepath)
    {
        if (!($file{0} == DIRECTORY_SEPARATOR || $file{0} == '/')
            // also account for C:\ style path
            && !($file{1} == ':' && ($file{2} == DIRECTORY_SEPARATOR || $file{2} == '/'))
        ) {
            if ($basepath != null) {
                $file = $basepath . DIRECTORY_SEPARATOR . $file;
            }
        }

        return $file;
    }

    /**
     * Gets value of specified var or NULL if var has not been put().
     * @param  string $name Variable name to retrieve.
     * @return mixed
     */
    public function get($name)
    {
        if (!isset($this->vars[$name])) {
            return null;
        }

        return $this->vars[$name];
    }

    /**
     * Merges in passed hash to vars array.
     *
     * Given an array like:
     *
     *            array(     'myvar' => 'Hello',
     *                    'myvar2' => 'Hello')
     *
     * Resulting template will have access to $myvar and $myvar2.
     *
     * @param  array   $vars
     * @param  boolean $recursiveMerge Should matching keys be recursively merged?
     * @return void
     */
    public function putAll($vars, $recursiveMerge = false)
    {
        if ($recursiveMerge) {
            $this->vars = array_merge_recursive($this->vars, $vars);
        } else {
            $this->vars = array_merge($this->vars, $vars);
        }
    }

    /**
     * Adds a variable to the context.
     *
     * Resulting template will have access to ${$name$} variable.
     *
     * @param string $name
     * @param mixed  $value
     */
    public function put($name, $value)
    {
        $this->vars[$name] = $value;
    }

    /**
     * Put a variable into the context, assigning it by reference.
     * This means that if the template modifies the variable, then it
     * will also be modified in the context.
     *
     * @param $name
     * @param &$value
     */
    public function putRef($name, &$value)
    {
        $this->vars[$name] = & $value;
    }

    /**
     * Makes a copy of the value and puts it into the context.
     * This is primarily to force copying (cloning) of objects, rather
     * than the default behavior which is to assign them by reference.
     * @param string $name
     * @param mixed  $value
     */
    public function putCopy($name, $value)
    {
        if (is_object($value)) {
            $value = clone $value;
        }
        $this->vars[$name] = $value;
    }

}
<?php
/*
 * $Id: eccc8da05ad1fbe73d38da7fca7ecb4816beabd0 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/listener/DefaultLogger.php';
include_once 'phing/system/util/Properties.php';

/**
 * Uses ANSI Color Code Sequences to colorize messages
 * sent to the console.
 *
 * If used with the -logfile option, the output file
 * will contain all the necessary escape codes to
 * display the text in colorized mode when displayed
 * in the console using applications like cat, more,
 * etc.
 *
 * This is designed to work on terminals that support ANSI
 * color codes.  It works on XTerm, ETerm, Mindterm, etc.
 * It also works on Win9x (with ANSI.SYS loaded.)
 *
 * NOTE:
 * It doesn't work on WinNT's COMMAND.COM even with
 * ANSI.SYS loaded.
 *
 * The default colors used for differentiating
 * the message levels can be changed by editing the
 * phing/listener/defaults.properties file.
 *
 * This file contains 5 key/value pairs:
 * AnsiColorLogger.ERROR_COLOR=2;31
 * AnsiColorLogger.WARNING_COLOR=2;35
 * AnsiColorLogger.INFO_COLOR=2;36
 * AnsiColorLogger.VERBOSE_COLOR=2;32
 * AnsiColorLogger.DEBUG_COLOR=2;34
 *
 * Another option is to pass a system variable named
 * ant.logger.defaults, with value set to the path of
 * the file that contains user defined Ansi Color
 * Codes, to the <B>java</B> command using -D option.
 *
 * To change these colors use the following chart:
 *
 *      <B>ANSI COLOR LOGGER CONFIGURATION</B>
 *
 * Format for AnsiColorLogger.*=
 *  Attribute;Foreground;Background
 *
 *  Attribute is one of the following:
 *  0 -> Reset All Attributes (return to normal mode)
 *  1 -> Bright (Usually turns on BOLD)
 *  2 -> Dim
 *  3 -> Underline
 *  5 -> link
 *  7 -> Reverse
 *  8 -> Hidden
 *
 *  Foreground is one of the following:
 *  30 -> Black
 *  31 -> Red
 *  32 -> Green
 *  33 -> Yellow
 *  34 -> Blue
 *  35 -> Magenta
 *  36 -> Cyan
 *  37 -> White
 *
 *  Background is one of the following:
 *  40 -> Black
 *  41 -> Red
 *  42 -> Green
 *  43 -> Yellow
 *  44 -> Blue
 *  45 -> Magenta
 *  46 -> Cyan
 *  47 -> White
 *
 * @author     Hans Lellelid <hans@xmpl.org> (Phing)
 * @author     Magesh Umasankar (Ant)
 * @package    phing.listener
 * @version    $Id: eccc8da05ad1fbe73d38da7fca7ecb4816beabd0 $
 */
class AnsiColorLogger extends DefaultLogger
{

    const ATTR_NORMAL = 0;
    const ATTR_BRIGHT = 1;
    const ATTR_DIM = 2;
    const ATTR_UNDERLINE = 3;
    const ATTR_BLINK = 5;
    const ATTR_REVERSE = 7;
    const ATTR_HIDDEN = 8;

    const FG_BLACK = 30;
    const FG_RED = 31;
    const FG_GREEN = 32;
    const FG_YELLOW = 33;
    const FG_BLUE = 34;
    const FG_MAGENTA = 35;
    const FG_CYAN = 36;
    const FG_WHITE = 37;

    const BG_BLACK = 40;
    const BG_RED = 41;
    const BG_GREEN = 42;
    const BG_YELLOW = 44;
    const BG_BLUE = 44;
    const BG_MAGENTA = 45;
    const BG_CYAN = 46;
    const BG_WHITE = 47;

    const PREFIX = "\x1b[";
    const SUFFIX = "m";
    const SEPARATOR = ';';
    const END_COLOR = "\x1b[0m"; // self::PREFIX . self::SUFFIX;

    private $errColor;
    private $warnColor;
    private $infoColor;
    private $verboseColor;
    private $debugColor;

    private $colorsSet = false;

    /**
     * Construct new AnsiColorLogger
     * Perform initializations that cannot be done in var declarations.
     */
    public function __construct()
    {
        parent::__construct();
        $this->errColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_RED . self::SUFFIX;
        $this->warnColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_MAGENTA . self::SUFFIX;
        $this->infoColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_CYAN . self::SUFFIX;
        $this->verboseColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_GREEN . self::SUFFIX;
        $this->debugColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_BLUE . self::SUFFIX;
    }

    /**
     * Set the colors to use from a property file specified by the
     * special ant property ant.logger.defaults
     */
    final private function setColors()
    {

        $userColorFile = Phing::getProperty("phing.logger.defaults");
        $systemColorFile = new PhingFile(Phing::getResourcePath("phing/listener/defaults.properties"));

        $in = null;

        try {
            $prop = new Properties();

            if ($userColorFile !== null) {
                $prop->load($userColorFile);
            } else {
                $prop->load($systemColorFile);
            }

            $err = $prop->getProperty("AnsiColorLogger.ERROR_COLOR");
            $warn = $prop->getProperty("AnsiColorLogger.WARNING_COLOR");
            $info = $prop->getProperty("AnsiColorLogger.INFO_COLOR");
            $verbose = $prop->getProperty("AnsiColorLogger.VERBOSE_COLOR");
            $debug = $prop->getProperty("AnsiColorLogger.DEBUG_COLOR");
            if ($err !== null) {
                $this->errColor = self::PREFIX . $err . self::SUFFIX;
            }
            if ($warn !== null) {
                $this->warnColor = self::PREFIX . $warn . self::SUFFIX;
            }
            if ($info !== null) {
                $this->infoColor = self::PREFIX . $info . self::SUFFIX;
            }
            if ($verbose !== null) {
                $this->verboseColor = self::PREFIX . $verbose . self::SUFFIX;
            }
            if ($debug !== null) {
                $this->debugColor = self::PREFIX . $debug . self::SUFFIX;
            }
        } catch (IOException $ioe) {
            //Ignore exception - we will use the defaults.
        }
    }

    /**
     * @see DefaultLogger#printMessage
     * @param string       $message
     * @param OutputStream $stream
     * @param int          $priority
     */
    final protected function printMessage($message, OutputStream $stream, $priority)
    {
        if ($message !== null) {

            if (!$this->colorsSet) {
                $this->setColors();
                $this->colorsSet = true;
            }

            switch ($priority) {
                case Project::MSG_ERR:
                    $message = $this->errColor . $message . self::END_COLOR;
                    break;
                case Project::MSG_WARN:
                    $message = $this->warnColor . $message . self::END_COLOR;
                    break;
                case Project::MSG_INFO:
                    $message = $this->infoColor . $message . self::END_COLOR;
                    break;
                case Project::MSG_VERBOSE:
                    $message = $this->verboseColor . $message . self::END_COLOR;
                    break;
                case Project::MSG_DEBUG:
                    $message = $this->debugColor . $message . self::END_COLOR;
                    break;
            }

            $stream->write($message . PHP_EOL);
        }
    }
}
<?php
/*
 *  $Id: 25f84e8447739435ac449a31307150bd6d8ea62a $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/listener/StreamRequiredBuildLogger.php';
include_once 'phing/BuildEvent.php';

/**
 * Writes a build event to the console.
 *
 * Currently, it only writes which targets are being executed, and
 * any messages that get logged.
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: 25f84e8447739435ac449a31307150bd6d8ea62a $
 * @see       BuildEvent
 * @package   phing.listener
 */
class DefaultLogger implements StreamRequiredBuildLogger
{

    /**
     *  Size of the left column in output. The default char width is 12.
     * @var int
     */
    const LEFT_COLUMN_SIZE = 12;

    /**
     *  The message output level that should be used. The default is
     *  <code>Project::MSG_VERBOSE</code>.
     * @var int
     */
    protected $msgOutputLevel = Project::MSG_ERR;

    /**
     *  Time that the build started
     * @var int
     */
    protected $startTime;

    /**
     * @var OutputStream Stream to use for standard output.
     */
    protected $out;

    /**
     * @var OutputStream Stream to use for error output.
     */
    protected $err;

    protected $emacsMode = false;

    /**
     *  Construct a new default logger.
     */
    public function __construct()
    {

    }

    /**
     *  Set the msgOutputLevel this logger is to respond to.
     *
     *  Only messages with a message level lower than or equal to the given
     *  level are output to the log.
     *
     *  <p> Constants for the message levels are in Project.php. The order of
     *  the levels, from least to most verbose, is:
     *
     *  <ul>
     *    <li>Project::MSG_ERR</li>
     *    <li>Project::MSG_WARN</li>
     *    <li>Project::MSG_INFO</li>
     *    <li>Project::MSG_VERBOSE</li>
     *    <li>Project::MSG_DEBUG</li>
     *  </ul>
     *
     *  The default message level for DefaultLogger is Project::MSG_ERR.
     *
     * @param int $level The logging level for the logger.
     * @see BuildLogger#setMessageOutputLevel()
     */
    public function setMessageOutputLevel($level)
    {
        $this->msgOutputLevel = (int) $level;
    }

    /**
     * Sets the output stream.
     * @param OutputStream $output
     * @see BuildLogger#setOutputStream()
     */
    public function setOutputStream(OutputStream $output)
    {
        $this->out = $output;
    }

    /**
     * Sets the error stream.
     * @param OutputStream $err
     * @see BuildLogger#setErrorStream()
     */
    public function setErrorStream(OutputStream $err)
    {
        $this->err = $err;
    }

    /**
     * Sets this logger to produce emacs (and other editor) friendly output.
     *
     * @param bool $emacsMode <code>true</code> if output is to be unadorned so that
     *                  emacs and other editors can parse files names, etc.
     */
    public function setEmacsMode($emacsMode)
    {
        $this->emacsMode = $emacsMode;
    }

    /**
     *  Sets the start-time when the build started. Used for calculating
     *  the build-time.
     *
     * @param BuildEvent $event
     */
    public function buildStarted(BuildEvent $event)
    {
        $this->startTime = Phing::currentTimeMillis();
        if ($this->msgOutputLevel >= Project::MSG_INFO) {
            $this->printMessage(
                "Buildfile: " . $event->getProject()->getProperty("phing.file"),
                $this->out,
                Project::MSG_INFO
            );
        }
    }

    /**
     *  Prints whether the build succeeded or failed, and any errors that
     *  occurred during the build. Also outputs the total build-time.
     *
     * @param BuildEvent $event
     * @see    BuildEvent::getException()
     */
    public function buildFinished(BuildEvent $event)
    {
        $error = $event->getException();
        if ($error === null) {
            $msg = PHP_EOL . $this->getBuildSuccessfulMessage() . PHP_EOL;
        } else {
            $msg = PHP_EOL . $this->getBuildFailedMessage() . PHP_EOL;
            self::throwableMessage($msg, $error, Project::MSG_VERBOSE <= $this->msgOutputLevel);
        }
        $msg .= PHP_EOL . "Total time: " . self::formatTime(Phing::currentTimeMillis() - $this->startTime) . PHP_EOL;

        if ($error === null) {
            $this->printMessage($msg, $this->out, Project::MSG_VERBOSE);
        } else {
            $this->printMessage($msg, $this->err, Project::MSG_ERR);
        }
    }

    public static function throwableMessage(&$msg, $error, $verbose)
    {
        while ($error instanceof BuildException) {
            $cause = $error->getCause();
            if ($cause === null) {
                break;
            }
            $msg1 = (string) $error;
            $msg2 = (string) $cause;
            if (StringHelper::endsWith($msg2, $msg1)) {
                $msg .= StringHelper::substring($msg1, 0, strlen($msg1) - strlen($msg2));
                $error = $cause;
            } else {
                break;
            }
        }
        if ($verbose || !($error instanceof BuildException)) {
            $msg .= (string) $error;
        } else {
            $msg .= $error->getMessage() . PHP_EOL;
        }
    }

    /**
     * Get the message to return when a build failed.
     * @return string The classic "BUILD FAILED"
     */
    protected function getBuildFailedMessage()
    {
        return "BUILD FAILED";
    }

    /**
     * Get the message to return when a build succeeded.
     * @return string The classic "BUILD FINISHED"
     */
    protected function getBuildSuccessfulMessage()
    {
        return "BUILD FINISHED";
    }

    /**
     *  Prints the current target name
     *
     * @param BuildEvent $event
     * @see    BuildEvent::getTarget()
     */
    public function targetStarted(BuildEvent $event)
    {
        if (Project::MSG_INFO <= $this->msgOutputLevel
            && $event->getTarget()->getName() != ''
        ) {
            $showLongTargets = $event->getProject()->getProperty("phing.showlongtargets");
            $msg = PHP_EOL . $event->getProject()->getName() . ' > ' . $event->getTarget()->getName(
                ) . ($showLongTargets ? ' [' . $event->getTarget()->getDescription() . ']' : '') . ':' . PHP_EOL;
            $this->printMessage($msg, $this->out, $event->getPriority());
        }
    }

    /**
     *  Fired when a target has finished. We don't need specific action on this
     *  event. So the methods are empty.
     *
     * @param BuildEvent $event
     * @see    BuildEvent::getException()
     */
    public function targetFinished(BuildEvent $event)
    {
    }

    /**
     *  Fired when a task is started. We don't need specific action on this
     *  event. So the methods are empty.
     *
     * @param BuildEvent $event
     * @see    BuildEvent::getTask()
     */
    public function taskStarted(BuildEvent $event)
    {
    }

    /**
     *  Fired when a task has finished. We don't need specific action on this
     *  event. So the methods are empty.
     *
     * @param  BuildEvent $event  The BuildEvent
     * @see    BuildEvent::getException()
     */
    public function taskFinished(BuildEvent $event)
    {
    }

    /**
     *  Print a message to the stdout.
     *
     * @param BuildEvent $event
     * @see    BuildEvent::getMessage()
     */
    public function messageLogged(BuildEvent $event)
    {
        $priority = $event->getPriority();
        if ($priority <= $this->msgOutputLevel) {
            $msg = "";
            if ($event->getTask() !== null && !$this->emacsMode) {
                $name = $event->getTask();
                $name = $name->getTaskName();
                $msg = str_pad("[$name] ", self::LEFT_COLUMN_SIZE, " ", STR_PAD_LEFT);
            }

            $msg .= $event->getMessage();

            if ($priority != Project::MSG_ERR) {
                $this->printMessage($msg, $this->out, $priority);
            } else {
                $this->printMessage($msg, $this->err, $priority);
            }
        }
    }

    /**
     *  Formats a time micro integer to human readable format.
     *
     * @param  integer The time stamp
     * @return string
     */
    public static function formatTime($micros)
    {
        $seconds = $micros;
        $minutes = (int)floor($seconds / 60);
        if ($minutes >= 1) {
            return sprintf(
                "%1.0f minute%s %0.2f second%s",
                $minutes,
                ($minutes === 1 ? " " : "s "),
                $seconds - floor($seconds / 60) * 60,
                ($seconds % 60 === 1 ? "" : "s")
            );
        } else {
            return sprintf("%0.4f second%s", $seconds, ($seconds % 60 === 1 ? "" : "s"));
        }
    }

    /**
     * Prints a message to console.
     *
     * @param  string $message The message to print.
     *                            Should not be <code>null</code>.
     * @param OutputStream|resource $stream The stream to use for message printing.
     * @param  int $priority The priority of the message.
     *                            (Ignored in this implementation.)
     * @throws IOException
     * @return void
     */
    protected function printMessage($message, OutputStream $stream, $priority)
    {
        $stream->write($message . PHP_EOL);
    }
}
<?php
/*
 * $Id: 5288eb0042a1c3bf2b1d34ed509a6c46adce87fd $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/listener/DefaultLogger.php';
include_once 'phing/system/util/Properties.php';

/**
 * Uses CSS class that must be defined in the HTML page
 * where the Phing output is displayed.
 *
 * If used with the -logfile option, the output
 * will contain the text wrapped in html <span> elements
 * with those css classes.
 *
 * The default classes used for differentiating
 * the message levels can be changed by editing the
 * phing/listener/defaults.properties file.
 *
 * This file can contain 5 key/value pairs:
 * HtmlColorLogger.ERROR_CLASS=_your_css_class_name_
 * HtmlColorLogger.WARNING_CLASS=_your_css_class_name_
 * HtmlColorLogger.INFO_CLASS=_your_css_class_name_
 * HtmlColorLogger.VERBOSE_CLASS=_your_css_class_name_
 * HtmlColorLogger.DEBUG_CLASS=_your_css_class_name_
 *
 * This stems from the Ansi Color Logger done by Hans Lellelid:
 *
 * @author     Anton Stöckl <anton@stoeckl.de> (Phing HTML Color Logger)
 * @author     Hans Lellelid <hans@xmpl.org> (Phing Ansi Color Logger)
 * @author     Magesh Umasankar (Ant)
 * @package    phing.listener
 * @version    $Id: 5288eb0042a1c3bf2b1d34ed509a6c46adce87fd $
 */
class HtmlColorLogger extends DefaultLogger
{

    const CLASS_ERR = 'phing_err';
    const CLASS_VERBOSE = 'phing_verbose';
    const CLASS_DEBUG = 'phing_debug';
    const CLASS_WARN = 'phing_warn';
    const CLASS_INFO = 'phing_info';

    const PREFIX = '<span class="';
    const SUFFIX = '">';
    const END_COLOR = '</span>';

    private $errColor;
    private $warnColor;
    private $infoColor;
    private $verboseColor;
    private $debugColor;

    private $colorsSet = false;

    /**
     * Construct new HtmlColorLogger
     * Perform initializations that cannot be done in var declarations.
     */
    public function __construct()
    {
        parent::__construct();
        $this->errColor = self::PREFIX . self::CLASS_ERR . self::SUFFIX;
        $this->warnColor = self::PREFIX . self::CLASS_WARN . self::SUFFIX;
        $this->infoColor = self::PREFIX . self::CLASS_INFO . self::SUFFIX;
        $this->verboseColor = self::PREFIX . self::CLASS_VERBOSE . self::SUFFIX;
        $this->debugColor = self::PREFIX . self::CLASS_DEBUG . self::SUFFIX;
    }

    /**
     * Set the colors to use from a property file specified in the
     * special phing property file "phing/listener/defaults.properties".
     */
    final private function setColors()
    {

        $systemColorFile = new PhingFile(Phing::getResourcePath("phing/listener/defaults.properties"));

        try {
            $prop = new Properties();

            $prop->load($systemColorFile);

            $err = $prop->getProperty("HtmlColorLogger.ERROR_CLASS");
            $warn = $prop->getProperty("HtmlColorLogger.WARNING_CLASS");
            $info = $prop->getProperty("HtmlColorLogger.INFO_CLASS");
            $verbose = $prop->getProperty("HtmlColorLogger.VERBOSE_CLASS");
            $debug = $prop->getProperty("HtmlColorLogger.DEBUG_CLASS");
            if ($err !== null) {
                $this->errColor = self::PREFIX . $err . self::SUFFIX;
            }
            if ($warn !== null) {
                $this->warnColor = self::PREFIX . $warn . self::SUFFIX;
            }
            if ($info !== null) {
                $this->infoColor = self::PREFIX . $info . self::SUFFIX;
            }
            if ($verbose !== null) {
                $this->verboseColor = self::PREFIX . $verbose . self::SUFFIX;
            }
            if ($debug !== null) {
                $this->debugColor = self::PREFIX . $debug . self::SUFFIX;
            }
        } catch (IOException $ioe) {
            //Ignore exception - we will use the defaults.
        }
    }

    /**
     * @see DefaultLogger#printMessage
     * @param string       $message
     * @param OutputStream $stream
     * @param int          $priority
     */
    final protected function printMessage($message, OutputStream $stream, $priority)
    {
        if ($message !== null) {

            if (!$this->colorsSet) {
                $this->setColors();
                $this->colorsSet = true;
            }

            $search = array('<', '>');
            $replace = array('&lt;', '&gt;');
            $message = str_replace($search, $replace, $message);

            $search = array("\t", "\n", "\r");
            $replace = array('&nbsp;&nbsp;&nbsp;', '<br>', '');
            $message = str_replace($search, $replace, $message);

            if (preg_match('@^( +)([^ ].+)@', $message, $matches)) {
                $len = strlen($matches[1]);
                $space = '&nbsp;';
                for ($i = 1; $i < $len; $i++) {
                    $space .= '&nbsp;';
                }
                $message = $space . $matches[2];
            }

            switch ($priority) {
                case Project::MSG_ERR:
                    $message = $this->errColor . $message . self::END_COLOR;
                    break;
                case Project::MSG_WARN:
                    $message = $this->warnColor . $message . self::END_COLOR;
                    break;
                case Project::MSG_INFO:
                    $message = $this->infoColor . $message . self::END_COLOR;
                    break;
                case Project::MSG_VERBOSE:
                    $message = $this->verboseColor . $message . self::END_COLOR;
                    break;
                case Project::MSG_DEBUG:
                    $message = $this->debugColor . $message . self::END_COLOR;
                    break;
            }

            $stream->write($message . '<br/>');
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/listener/XmlLogger.php';

/**
 * Generates a file in the current directory with
 * an JSON description of what happened during a build.
 * The default filename is "log.json", but this can be overridden
 * with the property <code>JsonLogger.file</code>.
 *
 * @author  Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.listener
 */
class JsonLogger extends XmlLogger
{
    /**
     * Fired when the build finishes, this adds the time taken and any
     * error stacktrace to the build element and writes the document to disk.
     *
     * @param BuildEvent $event An event with any relevant extra information.
     *                          Will not be <code>null</code>.
     * @throws BuildException
     */
    public function buildFinished(BuildEvent $event)
    {
        $elapsedTime = Phing::currentTimeMillis() - $this->getBuildTimerStart();

        $this->getBuildElement()->setAttribute(XmlLogger::TIME_ATTR, DefaultLogger::formatTime($elapsedTime));

        if ($event->getException() != null) {
            $this->getBuildElement()->setAttribute(XmlLogger::ERROR_ATTR, $event->getException()->getMessage());
            $errText = $this->getDoc()->createCDATASection($event->getException()->getTraceAsString());
            $stacktrace = $this->getDoc()->createElement(XmlLogger::STACKTRACE_TAG);
            $stacktrace->appendChild($errText);
            $this->getBuildElement()->appendChild($stacktrace);
        }

        $this->getDoc()->appendChild($this->getBuildElement());

        $outFilename = $event->getProject()->getProperty("JsonLogger.file");
        if ($outFilename == null) {
            $outFilename = "log.json";
        }

        try {
            $stream = $this->getOut();
            if ($stream === null) {
                $stream = new FileOutputStream($outFilename);
            }

            $writer = new OutputStreamWriter($stream);
            $writer->write($this->xml2js(simplexml_import_dom($this->getDoc())));
            $writer->close();
        } catch (IOException $exc) {
            try {
                $stream->close(); // in case there is a stream open still ...
            } catch (Exception $x) {
            }
            throw new BuildException("Unable to write log file.", $exc);
        }

        // cleanup:remove the buildElement
        $this->setBuildElement(null);

        array_pop($this->getElementStack());
        array_pop($this->getTimesStack());
    }

    private function xml2js(SimpleXMLElement $xmlnode, $isRoot = true)
    {
        $jsnode = array();

        if (!$isRoot) {
            if (count($xmlnode->attributes()) > 0){
                $jsnode["@attribute"] = array();
                foreach($xmlnode->attributes() as $key => $value)
                    $jsnode["@attribute"][$key] = (string)$value;
            }

            $textcontent = trim((string) $xmlnode);
            if (count($textcontent) > 0)
                $jsnode['_'] = $textcontent;

            foreach ($xmlnode->children() as $childxmlnode) {
                $childname = $childxmlnode->getName();
                if (!array_key_exists($childname, $jsnode))
                    $jsnode[$childname] = array();
                array_push($jsnode[$childname], $this->xml2js($childxmlnode, false));
            }
            return $jsnode;
        } else {
            $nodename = $xmlnode->getName();
            $jsnode[$nodename] = array();
            array_push($jsnode[$nodename], $this->xml2js($xmlnode, false));
            return json_encode($jsnode, JSON_PRETTY_PRINT);
        }
    }
}
<?php
/**
 * $Id: 63733db47089bd288aa4d5aacaa47de012b20eb4 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/listener/DefaultLogger.php';
include_once 'phing/system/util/Properties.php';
include_once 'phing/util/StringHelper.php';

/**
 * Uses PEAR Mail package to send the build log to one or
 * more recipients.
 *
 * @author     Michiel Rook <mrook@php.net>
 * @package    phing.listener
 * @version    $Id: 63733db47089bd288aa4d5aacaa47de012b20eb4 $
 */
class MailLogger extends DefaultLogger
{
    private $mailMessage = '';

    private $from = 'phing@phing.info';

    private $tolist;

    /**
     * Construct new MailLogger
     */
    public function __construct()
    {
        parent::__construct();

        @require_once 'Mail.php';

        if (!class_exists('Mail')) {
            throw new BuildException('Need the PEAR Mail package to send logs');
        }

        $tolist  = Phing::getDefinedProperty('phing.log.mail.recipients');

        if (!empty($tolist)) {
            $this->tolist = $tolist;
        }
    }

    /**
     * @see DefaultLogger::printMessage
     * @param string $message
     * @param OutputStream $stream
     * @param int $priority
     */
    final protected function printMessage($message, OutputStream $stream, $priority)
    {
        if ($message !== null) {
            $this->mailMessage .= $message . "\n";
        }
    }

    /**
     * Sends the mail
     *
     * @see DefaultLogger#buildFinished
     * @param BuildEvent $event
     */
    public function buildFinished(BuildEvent $event)
    {
        parent::buildFinished($event);

        $project = $event->getProject();
        $properties = $project->getProperties();

        $filename = $properties['phing.log.mail.properties.file'];

        // overlay specified properties file (if any), which overrides project
        // settings
        $fileProperties = new Properties();
        $file = new PhingFile($filename);

        try {
            $fileProperties->load($file);
        } catch (IOException $ioe) {
            // ignore because properties file is not required
        }

        foreach ($fileProperties as $key => $value) {
            $properties['key'] = $project->replaceProperties($value);
        }

        $success = $event->getException() === null;
        $prefix = $success ? 'success' : 'failure';

        try {
            $notify = StringHelper::booleanValue($this->getValue($properties, $prefix . '.notify', 'on'));
            if (!$notify) {
                return;
            }

            if (is_string(Phing::getDefinedProperty('phing.log.mail.subject'))) {
                $defaultSubject = Phing::getDefinedProperty('phing.log.mail.subject');
            } else {
                $defaultSubject = ($success) ? 'Build Success' : 'Build Failure';
            }
            $hdrs = array();
            $hdrs['From']     = $this->getValue($properties, 'from', $this->from);
            $hdrs['Reply-To'] = $this->getValue($properties, 'replyto', '');
            $hdrs['Cc']       = $this->getValue($properties, $prefix . '.cc', '');
            $hdrs['Bcc']      = $this->getValue($properties, $prefix . '.bcc', '');
            $hdrs['Body']     = $this->getValue($properties, $prefix . '.body', '');
            $hdrs['Subject']  = $this->getValue($properties, $prefix . '.subject', $defaultSubject);
            $tolist           = $this->getValue($properties, $prefix . '.to', $this->tolist);
        } catch (BadMethodCallException $e) {
            $project->log($e->getMessage(), Project::MSG_WARN);
        }

        if (empty($tolist)) {
            return;
        }

        $mail = Mail::factory('mail');
        $mail->send($tolist, $hdrs, $this->mailMessage);
    }

    /**
     * @param array $properties
     * @param string $name
     * @param mixed $defaultValue
     *
     * @return mixed
     *
     * @throws BadMethodCallException
     */
    private function getValue(array $properties, $name, $defaultValue)
    {
        $propertyName = 'phing.log.mail.' . $name;
        $value = $properties[$propertyName];
        if ($value === null) {
            $value = $defaultValue;

        }
        if ($value === null) {
            throw new BadMethodCallException('Missing required parameter: ' . $propertyName);

        }
        return $value;
    }
}
<?php
/*
 * $Id: fde85729a4da71d69341a94fb05cbaa1f7c40d5b $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/listener/DefaultLogger.php';

/**
 * Extends DefaultLogger to strip out empty targets.
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: fde85729a4da71d69341a94fb05cbaa1f7c40d5b $
 * @package   phing.listener
 */
class NoBannerLogger extends DefaultLogger
{

    private $targetName = null;

    /**
     * @param BuildEvent $event
     */
    public function targetStarted(BuildEvent $event)
    {
        $target = $event->getTarget();
        $this->targetName = $target->getName();
    }

    /**
     * @param BuildEvent $event
     */
    public function targetFinished(BuildEvent $event)
    {
        $this->targetName = null;
    }

    /**
     * @param BuildEvent $event
     */
    public function messageLogged(BuildEvent $event)
    {

        if ($event->getPriority() > $this->msgOutputLevel || null === $event->getMessage() || trim(
                $event->getMessage() === ""
            )
        ) {
            return;
        }

        if ($this->targetName !== null) {
            $msg = PHP_EOL . $event->getProject()->getName() . ' > ' . $this->targetName . ':' . PHP_EOL;
            $this->printMessage($msg, $this->out, $event->getPriority());
            $this->targetName = null;
        }

        parent::messageLogged($event);
    }
}
<?php
/*
 *  $Id: 63dcb060d3671bbf7d28433c4871c89c0119c5c1 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/BuildListener.php';

/**
 * Writes build messages to PEAR Log.
 *
 * By default it will log to file in current directory w/ name 'phing.log'.  You can customize
 * this behavior by setting properties:
 * - pear.log.type
 * - pear.log.name
 * - pear.log.ident (note that this class changes ident to project name)
 * - pear.log.conf (note that array values are currently unsupported in Phing property files)
 *
 * <code>
 *  phing -f build.xml -logger phing.listener.PearLogger -Dpear.log.type=file -Dpear.log.name=/path/to/log.log
 * </code>
 *
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: 63dcb060d3671bbf7d28433c4871c89c0119c5c1 $
 * @see       BuildEvent
 * @package   phing.listener
 */
class PearLogListener implements BuildListener
{

    /**
     *  Size of the left column in output. The default char width is 12.
     * @var int
     */
    const LEFT_COLUMN_SIZE = 12;

    /**
     *  Time that the build started
     * @var int
     */
    protected $startTime;

    /**
     * Maps Phing Project::MSG_* constants to PEAR_LOG_* constants.
     * @var array
     */
    protected static $levelMap = array(
        Project::MSG_DEBUG => PEAR_LOG_DEBUG,
        Project::MSG_INFO => PEAR_LOG_INFO,
        Project::MSG_VERBOSE => PEAR_LOG_NOTICE,
        Project::MSG_WARN => PEAR_LOG_WARNING,
        Project::MSG_ERR => PEAR_LOG_ERR
    );
    /**
     * Whether logging has been configured.
     * @var boolean
     */
    protected $logConfigured = false;

    /**
     * @var Log PEAR Log object.
     */
    protected $logger;

    /**
     * Configure the logger.
     */
    protected function configureLogging()
    {

        $type = Phing::getDefinedProperty('pear.log.type');
        $name = Phing::getDefinedProperty('pear.log.name');
        $ident = Phing::getDefinedProperty('pear.log.ident');
        $conf = Phing::getDefinedProperty('pear.log.conf');

        if ($type === null) {
            $type = 'file';
        }
        if ($name === null) {
            $name = 'phing.log';
        }
        if ($ident === null) {
            $ident = 'phing';
        }
        if ($conf === null) {
            $conf = array();
        }

        include_once 'Log.php';
        if (!class_exists('Log')) {
            throw new BuildException("Cannot find PEAR Log class for use by PearLogger.");
        }

        $this->logger = Log::singleton($type, $name, $ident, $conf, self::$levelMap[$this->msgOutputLevel]);
    }

    /**
     * Get the configured PEAR logger to use.
     * This method just ensures that logging has been configured and returns the configured logger.
     * @return Log
     */
    protected function logger()
    {
        if (!$this->logConfigured) {
            $this->configureLogging();
        }

        return $this->logger;
    }

    /**
     *  Sets the start-time when the build started. Used for calculating
     *  the build-time.
     *
     * @param  BuildEvent  The BuildEvent
     */
    public function buildStarted(BuildEvent $event)
    {
        $this->startTime = Phing::currentTimeMillis();
        $this->logger()->setIdent($event->getProject()->getName());
        $this->logger()->info("Starting build with buildfile: " . $event->getProject()->getProperty("phing.file"));
    }

    /**
     *  Logs whether the build succeeded or failed, and any errors that
     *  occurred during the build. Also outputs the total build-time.
     *
     * @param  BuildEvent  The BuildEvent
     * @see    BuildEvent::getException()
     */
    public function buildFinished(BuildEvent $event)
    {
        $error = $event->getException();
        if ($error === null) {
            $msg = "Finished successful build.";
        } else {
            $msg = "Build failed. [reason: " . $error->getMessage() . "]";
        }
        $this->logger()->log(
            $msg . " Total time: " . DefaultLogger::formatTime(Phing::currentTimeMillis() - $this->startTime)
        );
    }

    /**
     * Logs the current target name
     *
     * @param  BuildEvent  The BuildEvent
     * @see    BuildEvent::getTarget()
     */
    public function targetStarted(BuildEvent $event)
    {
    }

    /**
     *  Fired when a target has finished. We don't need specific action on this
     *  event. So the methods are empty.
     *
     * @param  BuildEvent  The BuildEvent
     * @see    BuildEvent::getException()
     */
    public function targetFinished(BuildEvent $event)
    {
    }

    /**
     *  Fired when a task is started. We don't need specific action on this
     *  event. So the methods are empty.
     *
     * @param  BuildEvent  The BuildEvent
     * @see    BuildEvent::getTask()
     */
    public function taskStarted(BuildEvent $event)
    {
    }

    /**
     *  Fired when a task has finished. We don't need specific action on this
     *  event. So the methods are empty.
     *
     * @param  BuildEvent  The BuildEvent
     * @see    BuildEvent::getException()
     */
    public function taskFinished(BuildEvent $event)
    {
    }

    /**
     *  Logs a message to the configured PEAR logger.
     *
     * @param  BuildEvent  The BuildEvent
     * @see    BuildEvent::getMessage()
     */
    public function messageLogged(BuildEvent $event)
    {
        if ($event->getPriority() <= $this->msgOutputLevel) {
            $msg = "";
            if ($event->getTask() !== null) {
                $name = $event->getTask();
                $name = $name->getTaskName();
                $msg = str_pad("[$name] ", self::LEFT_COLUMN_SIZE, " ", STR_PAD_LEFT);
            }
            $msg .= $event->getMessage();
            $this->logger()->log($msg, self::$levelMap[$event->getPriority()]);
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/listener/DefaultLogger.php';
include_once 'phing/BuildEvent.php';

/**
 * This is a special logger that is designed to profile builds.
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing.listener
 */
class ProfileLogger extends DefaultLogger
{
    private $profileData = array();

    protected static $dateFormat = DATE_RFC2822;

    /**
     * Logs a message to say that the target has started.
     *
     * @param BuildEvent $event
     *            An event with any relevant extra information. Must not be
     *            <code>null</code>.
     */
    public function targetStarted(BuildEvent $event)
    {
        if (@date_default_timezone_get() === 'UTC') {
            date_default_timezone_set('Europe/Berlin');
        }
        $now = Phing::currentTimeMillis();
        $name = "Target " . $event->getTarget()->getName();
        $this->logStart($event, $now, $name);
        $this->profileData[] = $now;
    }

    /**
     * Logs a message to say that the target has finished.
     *
     * @param BuildEvent $event
     *            An event with any relevant extra information. Must not be
     *            <code>null</code>.
     */
    public function targetFinished(BuildEvent $event)
    {
        $start = array_pop($this->profileData);

        $name = "Target " . $event->getTarget()->getName();
        $this->logFinish($event, $start, $name);
    }

    /**
     * Logs a message to say that the task has started.
     *
     * @param BuildEvent $event
     *            An event with any relevant extra information. Must not be
     *            <code>null</code>.
     */
    public function taskStarted(BuildEvent $event)
    {
        $name = $event->getTask()->getTaskName();
        $now = Phing::currentTimeMillis();
        $this->logStart($event, $now, $name);
        $this->profileData[] = $now;
    }

    /**
     * Logs a message to say that the task has finished.
     *
     * @param BuildEvent $event
     *            An event with any relevant extra information. Must not be
     *            <code>null</code>.
     */
    public function taskFinished(BuildEvent $event)
    {
        $start = array_pop($this->profileData);

        $name = $event->getTask()->getTaskName();
        $this->logFinish($event, $start, $name);
    }

    private function logFinish(BuildEvent $event, $start, $name)
    {
        $msg = null;
        if ($start != null) {
            $diff = self::formatTime(Phing::currentTimeMillis() - $start);
            $msg = Phing::getProperty("line.separator") . $name . ": finished "
                . date(self::$dateFormat, time()) . " ("
                . $diff
                . ")";
        } else {
            $msg = Phing::getProperty("line.separator") . $name . ": finished " . date(self::$dateFormat, time())
                . " (unknown duration, start not detected)";
        }
        $this->printMessage($msg, $this->out, $event->getPriority());
    }

    private function logStart(BuildEvent $event, $start, $name)
    {
        $msg = Phing::getProperty("line.separator") . $name . ": started " . date(self::$dateFormat, $start);
        $this->printMessage($msg, $this->out, $event->getPriority());
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/listener/DefaultLogger.php';

/**
 * A logger which logs nothing but build failure and what task might output.
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing.listener
 */
class SilentLogger extends DefaultLogger
{
    public function buildStarted(BuildEvent $event)
    {
        // log nothing
    }

    public function buildFinished(BuildEvent $event)
    {
        if ($event->getException() != null) {
            parent::buildFinished($event);
        }
    }

    public function targetStarted(BuildEvent $event)
    {
        // log nothing
    }

    public function targetFinished(BuildEvent $event)
    {
        // log nothing
    }

    public function taskStarted(BuildEvent $event)
    {
        // log nothing
    }

    public function taskFinished(BuildEvent $event)
    {
        // log nothing
    }
}
<?php
/*
 *  $Id: e02403f7640929e2b0650bcaf270709fe6281b3c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/BuildLogger.php';

/**
 * Interface for build loggers that require that out/err streams be set in order to function.
 *
 * This is just an empty sub-interface to BuildLogger, but is used by Phing to throw
 * graceful errors when classes like phing.listener.DefaultLogger are being used as
 * -listener.
 *
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: e02403f7640929e2b0650bcaf270709fe6281b3c $
 * @see       BuildEvent
 * @see       Project::addBuildListener()
 * @package   phing
 */
interface StreamRequiredBuildLogger extends BuildLogger
{

}
<?php
/*
 * $Id: e5ae611d970cd766f7f23fe5a70c6483e66aceb5 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/listener/AnsiColorLogger.php';

/**
 * Extends AnsiColorLogger to display times for each target
 *
 * @author    Patrick McAndrew <patrick@urg.name>
 * @copyright 2013. All rights reserved
 * @version   $Id: e5ae611d970cd766f7f23fe5a70c6483e66aceb5 $
 * @package   phing.listener
 */
class TargetLogger extends AnsiColorLogger
{

    private $targetName = null;
    private $targetStartTime;

    /**
     * @param BuildEvent $event
     */
    public function targetStarted(BuildEvent $event)
    {
        parent::targetStarted($event);
        $target = $event->getTarget();
        $this->targetName = $target->getName();
        $this->targetStartTime = Phing::currentTimeMillis();
    }

    /**
     * @param BuildEvent $event
     */
    public function targetFinished(BuildEvent $event)
    {
        $msg = PHP_EOL . "Target time: " . self::formatTime(
                Phing::currentTimeMillis() - $this->targetStartTime
            ) . PHP_EOL;
        $event->setMessage($msg, Project::MSG_INFO);
        $this->messageLogged($event);
        $this->targetName = null;

    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/listener/DefaultLogger.php';

/**
 * Like a normal logger, except with timed outputs.
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing.listener
 */
class TimestampedLogger extends DefaultLogger
{
    /**
     * what appears between the old message and the new
     */
    public static $SPACER = ' - at ';

    /**
     * This is an override point: the message that indicates whether a build failed.
     * Subclasses can change/enhance the message.
     *
     * @return string The classic "BUILD FAILED" plus a timestamp
     */
    protected function getBuildFailedMessage()
    {
        return parent::getBuildFailedMessage() . self::$SPACER . date('n/d/Y h:m a');
    }

    /**
     * This is an override point: the message that indicates that a build succeeded.
     * Subclasses can change/enhance the message.
     *
     * @return string The classic "BUILD SUCCESSFUL" plus a timestamp
     */
    protected function getBuildSuccessfulMessage()
    {
        return parent::getBuildSuccessfulMessage() . self::$SPACER . date('n/d/Y h:m a');
    }
}
<?php
/**
 * $Id: b7dcf7145fd5bda35ef858f56f574255e99ba617 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/BuildLogger.php';
require_once 'phing/listener/DefaultLogger.php';
require_once 'phing/system/util/Timer.php';

/**
 * Generates a file in the current directory with
 * an XML description of what happened during a build.
 * The default filename is "log.xml", but this can be overridden
 * with the property <code>XmlLogger.file</code>.
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: b7dcf7145fd5bda35ef858f56f574255e99ba617 $
 * @package phing.listener
 */
class XmlLogger implements BuildLogger
{

    /** XML element name for a build. */
    const BUILD_TAG = "build";

    /** XML element name for a target. */
    const TARGET_TAG = "target";

    /** XML element name for a task. */
    const TASK_TAG = "task";

    /** XML element name for a message. */
    const MESSAGE_TAG = "message";

    /** XML attribute name for a name. */
    const NAME_ATTR = "name";

    /** XML attribute name for a time. */
    const TIME_ATTR = "time";

    /** XML attribute name for a message priority. */
    const PRIORITY_ATTR = "priority";

    /** XML attribute name for a file location. */
    const LOCATION_ATTR = "location";

    /** XML attribute name for an error description. */
    const ERROR_ATTR = "error";

    /** XML element name for a stack trace. */
    const STACKTRACE_TAG = "stacktrace";

    /**
     * @var DOMDocument The XML document created by this logger.
     */
    private $doc;

    /**
     * @var int Start time for entire build.
     */
    private $buildTimerStart = 0;

    /**
     * @var DOMElement Top-level (root) build element
     */
    private $buildElement;

    /**
     * @var array DOMElement[] The parent of the element being processed.
     */
    private $elementStack = array();

    /**
     * @var array int[] Array of millisecond times for the various elements being processed.
     */
    private $timesStack = array();

    /**
     * @var int
     */
    private $msgOutputLevel = Project::MSG_DEBUG;

    /**
     * @var OutputStream Stream to use for standard output.
     */
    private $out;

    /**
     * @var OutputStream Stream to use for error output.
     */
    private $err;

    /**
     * @var string Name of filename to create.
     */
    private $outFilename;

    /**
     *  Constructs a new BuildListener that logs build events to an XML file.
     */
    public function __construct()
    {
        $this->doc = new DOMDocument("1.0", "UTF-8");
        $this->doc->formatOutput = true;
    }

    /**
     * Fired when the build starts, this builds the top-level element for the
     * document and remembers the time of the start of the build.
     *
     * @param BuildEvent Ignored.
     */
    public function buildStarted(BuildEvent $event)
    {
        $this->buildTimerStart = Phing::currentTimeMillis();
        $this->buildElement = $this->doc->createElement(XmlLogger::BUILD_TAG);
        array_push($this->elementStack, $this->buildElement);
        array_push($this->timesStack, $this->buildTimerStart);
    }

    /**
     * Fired when the build finishes, this adds the time taken and any
     * error stacktrace to the build element and writes the document to disk.
     *
     * @param BuildEvent $event An event with any relevant extra information.
     *                          Will not be <code>null</code>.
     * @throws BuildException
     */
    public function buildFinished(BuildEvent $event)
    {
        $xslUri = $event->getProject()->getProperty("phing.XmlLogger.stylesheet.uri");
        if ($xslUri === null) {
            $xslUri = "";
        }

        if ($xslUri !== '') {
            $xslt = $this->doc->createProcessingInstruction('xml-stylesheet', 'type="text/xsl" href="' . $xslUri . '"');
            $this->doc->appendChild($xslt);
        }

        $elapsedTime = Phing::currentTimeMillis() - $this->buildTimerStart;

        $this->buildElement->setAttribute(XmlLogger::TIME_ATTR, DefaultLogger::formatTime($elapsedTime));

        if ($event->getException() != null) {
            $this->buildElement->setAttribute(XmlLogger::ERROR_ATTR, $event->getException()->getMessage());
            $errText = $this->doc->createCDATASection($event->getException()->getTraceAsString());
            $stacktrace = $this->doc->createElement(XmlLogger::STACKTRACE_TAG);
            $stacktrace->appendChild($errText);
            $this->buildElement->appendChild($stacktrace);
        }

        $this->doc->appendChild($this->buildElement);

        $outFilename = $event->getProject()->getProperty("XmlLogger.file");
        if ($outFilename == null) {
            $outFilename = "log.xml";
        }

        try {
            $stream = $this->out;
            if ($stream === null) {
                $stream = new FileOutputStream($outFilename);
            }

            // Yes, we could just stream->write() but this will eventually be the better
            // way to do this (when we need to worry about charset conversions.
            $writer = new OutputStreamWriter($stream);
            $writer->write($this->doc->saveXML());
            $writer->close();
        } catch (IOException $exc) {
            try {
                $stream->close(); // in case there is a stream open still ...
            } catch (Exception $x) {
            }
            throw new BuildException("Unable to write log file.", $exc);
        }

        // cleanup:remove the buildElement
        $this->buildElement = null;

        array_pop($this->elementStack);
        array_pop($this->timesStack);
    }

    /**
     * Fired when a target starts building, remembers the current time and the name of the target.
     *
     * @param BuildEvent $event An event with any relevant extra information.
     *                          Will not be <code>null</code>.
     */
    public function targetStarted(BuildEvent $event)
    {
        $target = $event->getTarget();

        $targetElement = $this->doc->createElement(XmlLogger::TARGET_TAG);
        $targetElement->setAttribute(XmlLogger::NAME_ATTR, $target->getName());

        array_push($this->timesStack, Phing::currentTimeMillis());
        array_push($this->elementStack, $targetElement);
    }

    /**
     * Fired when a target finishes building, this adds the time taken
     * to the appropriate target element in the log.
     *
     * @param BuildEvent $event An event with any relevant extra information.
     *                          Will not be <code>null</code>.
     */
    public function targetFinished(BuildEvent $event)
    {
        $targetTimerStart = array_pop($this->timesStack);
        $targetElement = array_pop($this->elementStack);

        $elapsedTime = Phing::currentTimeMillis() - $targetTimerStart;
        $targetElement->setAttribute(XmlLogger::TIME_ATTR, DefaultLogger::formatTime($elapsedTime));

        $parentElement = $this->elementStack[count($this->elementStack) - 1];
        $parentElement->appendChild($targetElement);
    }

    /**
     * Fired when a task starts building, remembers the current time and the name of the task.
     *
     * @param BuildEvent $event An event with any relevant extra information.
     *                          Will not be <code>null</code>.
     */
    public function taskStarted(BuildEvent $event)
    {
        $task = $event->getTask();

        $taskElement = $this->doc->createElement(XmlLogger::TASK_TAG);
        $taskElement->setAttribute(XmlLogger::NAME_ATTR, $task->getTaskName());
        $taskElement->setAttribute(XmlLogger::LOCATION_ATTR, $task->getLocation()->toString());

        array_push($this->timesStack, Phing::currentTimeMillis());
        array_push($this->elementStack, $taskElement);
    }

    /**
     * Fired when a task finishes building, this adds the time taken
     * to the appropriate task element in the log.
     *
     * @param BuildEvent $event An event with any relevant extra information.
     *                          Will not be <code>null</code>.
     */
    public function taskFinished(BuildEvent $event)
    {
        $taskTimerStart = array_pop($this->timesStack);
        $taskElement = array_pop($this->elementStack);

        $elapsedTime = Phing::currentTimeMillis() - $taskTimerStart;
        $taskElement->setAttribute(XmlLogger::TIME_ATTR, DefaultLogger::formatTime($elapsedTime));

        $parentElement = $this->elementStack[count($this->elementStack) - 1];
        $parentElement->appendChild($taskElement);
    }

    /**
     * Fired when a message is logged, this adds a message element to the
     * most appropriate parent element (task, target or build) and records
     * the priority and text of the message.
     *
     * @param BuildEvent An event with any relevant extra information.
     *              Will not be <code>null</code>.
     */
    public function messageLogged(BuildEvent $event)
    {
        $priority = $event->getPriority();

        if ($priority > $this->msgOutputLevel) {
            return;
        }

        $messageElement = $this->doc->createElement(XmlLogger::MESSAGE_TAG);

        switch ($priority) {
            case Project::MSG_ERR:
                $name = "error";
                break;
            case Project::MSG_WARN:
                $name = "warn";
                break;
            case Project::MSG_INFO:
                $name = "info";
                break;
            default:
                $name = "debug";
                break;
        }

        $messageElement->setAttribute(XmlLogger::PRIORITY_ATTR, $name);

        if (function_exists('mb_convert_encoding')) {
            $messageConverted = mb_convert_encoding($event->getMessage(), 'UTF-8');
        } else {
            $messageConverted = utf8_encode($event->getMessage());
        }

        $messageText = $this->doc->createCDATASection($messageConverted);

        $messageElement->appendChild($messageText);

        if (!empty($this->elementStack)) {
            $this->elementStack[count($this->elementStack) - 1]->appendChild($messageElement);
        }
    }

    /**
     *  Set the msgOutputLevel this logger is to respond to.
     *
     *  Only messages with a message level lower than or equal to the given
     *  level are output to the log.
     *
     *  <p> Constants for the message levels are in Project.php. The order of
     *  the levels, from least to most verbose, is:
     *
     *  <ul>
     *    <li>Project::MSG_ERR</li>
     *    <li>Project::MSG_WARN</li>
     *    <li>Project::MSG_INFO</li>
     *    <li>Project::MSG_VERBOSE</li>
     *    <li>Project::MSG_DEBUG</li>
     *  </ul>
     *
     *  The default message level for DefaultLogger is Project::MSG_ERR.
     *
     * @param int $level The logging level for the logger.
     * @see BuildLogger#setMessageOutputLevel()
     */
    public function setMessageOutputLevel($level)
    {
        $this->msgOutputLevel = (int) $level;
    }

    /**
     * Sets the output stream.
     * @param OutputStream $output
     * @see BuildLogger#setOutputStream()
     */
    public function setOutputStream(OutputStream $output)
    {
        $this->out = $output;
    }

    /**
     * Sets the error stream.
     * @param OutputStream $err
     * @see BuildLogger#setErrorStream()
     */
    public function setErrorStream(OutputStream $err)
    {
        $this->err = $err;
    }

    /**
     * Sets this logger to produce emacs (and other editor) friendly output.
     *
     * @param bool $emacsMode true if output is to be unadorned so that emacs and other editors
     *                             can parse files names, etc.
     */
    public function setEmacsMode($emacsMode)
    {
    }

    /**
     * @return DOMDocument
     */
    public function getDoc()
    {
        return $this->doc;
    }

    /**
     * @return int
     */
    public function getBuildTimerStart()
    {
        return $this->buildTimerStart;
    }

    /**
     * @return DOMElement
     */
    public function getBuildElement()
    {
        return $this->buildElement;
    }

    public function setBuildElement($elem)
    {
        $this->buildElement = $elem;
    }


    /**
     * @return array
     */
    public function getElementStack()
    {
        return $this->elementStack;
    }

    /**
     * @return array
     */
    public function getTimesStack()
    {
        return $this->timesStack;
    }

    /**
     * @return int
     */
    public function getMsgOutputLevel()
    {
        return $this->msgOutputLevel;
    }

    /**
     * @return OutputStream
     */
    public function getOut()
    {
        return $this->out;
    }

    /**
     * @return OutputStream
     */
    public function getErr()
    {
        return $this->err;
    }

    /**
     * @return string
     */
    public function getOutFilename()
    {
        return $this->outFilename;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/mappers/ContainerMapper.php';

/**
 * A <code>ContainerMapper</code> that chains the results of the first
 * nested <code>FileNameMapper</code>s into sourcefiles for the second,
 * the second to the third, and so on, returning the resulting mapped
 * filenames from the last nested <code>FileNameMapper</code>.
 */
class ChainedMapper extends ContainerMapper
{
    /** {@inheritDoc}. */
    public function main($sourceFileName)
    {
        $results[] = $sourceFileName;
        $mapper = null;

        foreach ($this->getMappers() as $mapper) {
            if ($mapper !== null) {
                $inputs = $results;
                $results = array();

                foreach ($inputs as $input) {
                    $mapped = $mapper->getImplementation()->main($input);
                    if ($mapped != null) {
                        $results = $mapped;
                    }
                }
            }
        }
        return !empty($results) ? $results : null;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/mappers/ContainerMapper.php';

/**
 * A <code>ContainerMapper</code> that unites the results of its constituent
 * <code>FileNameMapper</code>s into a single set of result filenames.
 *
 * @author  Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.mappers
 */
class CompositeMapper extends ContainerMapper
{
    /** {@inheritDoc}. */
    public function main($sourceFileName)
    {
        $results = array();
        foreach ($this->getMappers() as $mapper) {
            $result = $mapper->getImplementation()->main($sourceFileName);
            if ($result === null) {
                continue;
            }
            $results[] = $result[0];
        }
        return !empty($results) ? $results : null;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/mappers/FileNameMapper.php';

/**
 * A <code>FileNameMapper</code> that contains
 * other <code>FileNameMapper</code>s.
 * @see FileNameMapper
 */
abstract class ContainerMapper implements FileNameMapper
{
    private $mappers = array();

    /**
     * Add a <code>Mapper</code>.
     * @param Mapper $mapper the <code>Mapper</code> to add.
     */
    public function addMapper(Mapper $mapper)
    {
        $this->add($mapper->getImplementation());
    }

    /**
     * An add configured version of the add method.
     * This class used to contain an add method and an
     * addConfiguredMapper method. Dur to ordering,
     * the add method was always called first. This
     * addConfigured method has been added to allow
     * chaining to work correctly.
     * @param FileNameMapper $fileNameMapper a <code>FileNameMapper</code>.
     */
    public function addConfigured(FileNameMapper $fileNameMapper)
    {
        $this->add($fileNameMapper);
    }

    /**
     * Add a <code>FileNameMapper</code>.
     * @param FileNameMapper $fileNameMapper a <code>FileNameMapper</code>.
     * @throws BadMethodCallException if attempting to add this
     *         <code>ContainerMapper</code> to itself, or if the specified
     *         <code>FileNameMapper</code> is itself a <code>ContainerMapper</code>
     *         that contains this <code>ContainerMapper</code>.
     */
    public function add(Mapper $fileNameMapper)
    {
        if ($this == $fileNameMapper || ($fileNameMapper instanceof ContainerMapper && $fileNameMapper->contains($this))) {
            throw new BadMethodCallException("Circular mapper containment condition detected");
        } else {
            $this->mappers[] = $fileNameMapper;
        }
    }

    /**
     * Return <code>true</code> if this <code>ContainerMapper</code> or any of
     * its sub-elements contains the specified <code>FileNameMapper</code>.
     * @param FileNameMapper $fileNameMapper   the <code>FileNameMapper</code> to search for.
     * @return boolean
     */
    protected function contains(FileNameMapper $fileNameMapper)
    {
        $foundit = false;
        for ($iter = new ArrayIterator($this->mappers); $iter->valid() && !$foundit;) {
            $iter->next();
            $next = $iter->current();
            $foundit = ($next == $fileNameMapper || ($next instanceof ContainerMapper && $next->contains($fileNameMapper)));
        }
        return $foundit;
    }

    /**
     * Get the <code>List</code> of <code>FileNameMapper</code>s.
     * @return FileNameMapper[]
     */
    public function getMappers()
    {
        return $this->mappers;
    }

    /**
     * Empty implementation.
     * @param string $ignore ignored.
     */
    public function setFrom($ignore)
    {
        //Empty
    }

    /**
     * Empty implementation.
     * @param string $ignore ignored.
     */
    public function setTo($ignore)
    {
        //Empty
    }
}
<?php
/**
 *  $Id: 01dfa75f67b0755485f8a096fed622a3d318388f $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/BuildException.php';
include_once 'phing/mappers/FileNameMapper.php';

/**
 * A mapper that strips of the a configurable number of leading
 * directories from a file name.
 *
 * @author  Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.mappers
 */
class CutDirsMapper implements FileNameMapper
{
    private $dirs = 0;

    /**
     * Empty implementation.
     * @param mixed $ignore ignored.
     */
    public function setFrom($ignore)
    {
    }

    /**
     * The number of leading directories to cut.
     * @param int $dirs
     */
    public function setTo($dirs)
    {
        $this->dirs = (int) $dirs;
    }

    /** {@inheritDoc}. */
    public function main($sourceFileName)
    {
        if ($this->dirs <= 0) {
            throw new BuildException('dirs must be set to a positive number');
        }
        $fileSep = PhingFile::$separator;
        $fileSepCorrected = str_replace(array('/', '\\'), $fileSep, $sourceFileName);
        $nthMatch = strpos($fileSepCorrected, $fileSep);

        for ($n = 1; $nthMatch > -1 && $n < $this->dirs; $n++) {
            $nthMatch = strpos($fileSepCorrected, $fileSep, $nthMatch + 1);
        }

        if ($nthMatch === false) {
            return null;
        }

        return array(substr($sourceFileName, $nthMatch + 1));
    }
}
<?php
/*
 *  $Id: a5a4cdae56af93ef6e8f999d3d29868ec5e049fe $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Interface for filename mapper classes.
 *
 * @author Andreas Aderhold, andi@binarycloud.com
 * @author Hans Lellelid <hans@xmpl.org>
 * @version $Id: a5a4cdae56af93ef6e8f999d3d29868ec5e049fe $
 * @package phing.mappers
 */
interface FileNameMapper
{

    /**
     * The mapper implementation.
     *
     * @param  mixed $sourceFileName The data the mapper works on.
     * @return array The data after the mapper has been applied; must be in array format (for some reason).
     */
    public function main($sourceFileName);

    /**
     * Accessor. Sets the to property. The actual implementation
     * depends on the child class.
     *
     * @param  string $to To what this mapper should convert the from string
     * @return void
     */
    public function setTo($to);

    /**
     * Accessor. Sets the from property. What this mapper should
     * recognize. The actual implementation is dependent upon the
     * child class
     *
     * @param  string $from On what this mapper should work
     * @return void
     */
    public function setFrom($from);

}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/mappers/ContainerMapper.php';

/**
 * A <code>ContainerMapper</code> that returns the results of its
 * first constituent <code>FileNameMapper</code>s that matches.
 *
 * @author  Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.mappers
 */
class FirstMatchMapper extends ContainerMapper
{
    /** {@inheritDoc}. */
    public function main($sourceFileName)
    {
        foreach ($this->getMappers() as $mapper) {
            if ($mapper !== null) {
                $mapped = $mapper->getImplementation()->main($sourceFileName);
                if ($mapped !== null) {
                    return $mapped;
                }
            }
        }
        return null;
    }
}
<?php
/**
 *  $Id: 8181ebf4f40374d9a706a4961a0e17f902fea000 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/mappers/FileNameMapper.php';

/**
 * Removes any directory information from the passed path.
 *
 * @author   Andreas Aderhold <andi@binarycloud.com>
 * @version  $Id: 8181ebf4f40374d9a706a4961a0e17f902fea000 $
 * @package  phing.mappers
 */
class FlattenMapper implements FileNameMapper
{
    /**
     * The mapper implementation. Returns string with source filename
     * but without leading directory information
     *
     * @param  string $sourceFileName The data the mapper works on
     * @return array  The data after the mapper has been applied
     */
    public function main($sourceFileName)
    {
        $f = new PhingFile($sourceFileName);

        return array($f->getName());
    }

    /**
     * Ignored here.
     * {@inheritdoc}
     * @param string $to
     * @return void
     */
    public function setTo($to)
    {
    }

    /**
     * Ignored here.
     * {@inheritdoc}
     * @param string $from
     * @return void
     */
    public function setFrom($from)
    {
    }
}
<?php
/*
 *  $Id: bbd21ce62611adac2da693d64941edf0272d7f72 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/mappers/FileNameMapper.php';

/**
 * Uses glob patterns to perform filename transformations.
 *
 * @author   Andreas Aderhold, andi@binarycloud.com
 * @version  $Id: bbd21ce62611adac2da693d64941edf0272d7f72 $
 * @package   phing.mappers
 */
class GlobMapper implements FileNameMapper
{
    /**
     * Part of &quot;from&quot; pattern before the <code>.*</code>.
     * @var string $fromPrefix
     */
    private $fromPrefix = null;

    /**
     * Part of &quot;from&quot; pattern after the <code>.*</code>.
     * @var string $fromPostfix
     */
    private $fromPostfix = null;

    /**
     * Length of the prefix (&quot;from&quot; pattern).
     * @var int $prefixLength
     */
    private $prefixLength;

    /**
     * Length of the postfix (&quot;from&quot; pattern).
     * @var int $postfixLength
     */
    private $postfixLength;

    /**
     * Part of &quot;to&quot; pattern before the <code>*.</code>.
     * @var string $toPrefix
     */
    private $toPrefix = null;

    /**
     * Part of &quot;to&quot; pattern after the <code>*.</code>.
     * @var string $toPostfix
     */
    private $toPostfix = null;

    /**
     * {@inheritdoc}
     * @param mixed $sourceFileName
     * @return array|null
     */
    public function main($sourceFileName)
    {
        if (($this->fromPrefix === null)
            || !StringHelper::startsWith($this->fromPrefix, $sourceFileName)
            || !StringHelper::endsWith($this->fromPostfix, $sourceFileName)
        ) {
            return null;
        }
        $varpart = $this->extractVariablePart($sourceFileName);
        $substitution = $this->toPrefix . $varpart . $this->toPostfix;

        return array($substitution);
    }

    /**
     * {@inheritdoc}
     * @param string $from
     * @return void
     */
    public function setFrom($from)
    {
        $index = strrpos($from, '*');

        if ($index === false) {
            $this->fromPrefix = $from;
            $this->fromPostfix = "";
        } else {
            $this->fromPrefix = substr($from, 0, $index);
            $this->fromPostfix = substr($from, $index + 1);
        }
        $this->prefixLength = strlen($this->fromPrefix);
        $this->postfixLength = strlen($this->fromPostfix);
    }

    /**
     * Sets the &quot;to&quot; pattern. Required.
     * {@inheritdoc}
     * @param string $to
     * @return void
     */
    public function setTo($to)
    {
        $index = strrpos($to, '*');
        if ($index === false) {
            $this->toPrefix = $to;
            $this->toPostfix = "";
        } else {
            $this->toPrefix = substr($to, 0, $index);
            $this->toPostfix = substr($to, $index + 1);
        }
    }

    /**
     * Extracts the variable part.
     * @param string $name
     * @return string
     */
    private function extractVariablePart($name)
    {
        // ergh, i really hate php's string functions .... all but natural
        $start = ($this->prefixLength === 0) ? 0 : $this->prefixLength;
        $end = ($this->postfixLength === 0) ? strlen($name) : strlen($name) - $this->postfixLength;
        $len = $end - $start;

        return substr($name, $start, $len);
    }
}
<?php
/**
 *  $Id: c534489effaaf9876a0ec7d087e5b37f8521c43c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/mappers/FileNameMapper.php';

/**
 * This mapper does nothing ;)
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: c534489effaaf9876a0ec7d087e5b37f8521c43c $
 * @package   phing.mappers
 */
class IdentityMapper implements FileNameMapper
{
    /**
     * The mapper implementation. Basically does nothing in this case.
     *
     * @param  string $sourceFileName The data the mapper works on.
     * @return array  The data after the mapper has been applied
     */
    public function main($sourceFileName)
    {
        return array($sourceFileName);
    }

    /**
     * Ignored here.
     * {@inheritdoc}
     * @param string $to
     * @return void
     */
    public function setTo($to)
    {
    }

    /**
     * Ignored here.
     * {@inheritdoc}
     * @param string $from
     * @return void
     */
    public function setFrom($from)
    {
    }
}
<?php
/*
 *  $Id: fd79adda1c462f048bfaee486b9d5fb3be7ff7e2 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/mappers/FileNameMapper.php';

/**
 * For merging files into a single file.  In practice just returns whatever value
 * was set for "to".
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @version   $Id: fd79adda1c462f048bfaee486b9d5fb3be7ff7e2 $
 * @package   phing.mappers
 */
class MergeMapper implements FileNameMapper
{

    /** the merge */
    private $mergedFile;

    /**
     * The mapper implementation. Basically does nothing in this case.
     *
     * @param mixed $sourceFileName The data the mapper works on
     * @throws BuildException
     * @return mixed The data after the mapper has been applied
     * @author  Andreas Aderhold, andi@binarycloud.com
     */
    public function main($sourceFileName)
    {
        if ($this->mergedFile === null) {
            throw new BuildException("MergeMapper error, to attribute not set");
        }

        return array($this->mergedFile);
    }

    /**
     * Accessor. Sets the to property
     *
     * @param   string     To what this mapper should convert the from string
     * @return boolean True
     * @author  Andreas Aderhold, andi@binarycloud.com
     */
    public function setTo($to)
    {
        $this->mergedFile = $to;
    }

    /**
     * Ignored.
     * @param string $from
     */
    public function setFrom($from)
    {
    }

}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/mappers/FileNameMapper.php';
include_once 'phing/util/StringHelper.php';
include_once 'phing/util/regexp/Regexp.php';

/**
 * Uses regular expressions to perform filename transformations.
 *
 * @author Andreas Aderhold <andi@binarycloud.com>
 * @author Hans Lellelid <hans@velum.net>
 *
 * @package phing.mappers
 */
class RegexpMapper implements FileNameMapper
{
    /**
     * @var string $to
     */
    private $to;

    /**
     * The Regexp engine.
     *
     * @var Regexp $reg
     */
    private $reg;

    /**
     * Instantiage regexp matcher here.
     */
    public function __construct()
    {
        $this->reg = new Regexp();
    }

    /**
     * Sets the &quot;from&quot; pattern. Required.
     * {@inheritdoc}
     *
     * @param string $from
     *
     * @return void
     */
    public function setFrom($from)
    {
        $this->reg->setPattern($from);
    }

    /**
     * Sets the &quot;to&quot; pattern. Required.
     *
     * {@inheritdoc}
     *
     * @param string $to
     *
     * @return void
     *
     * @intern [HL] I'm changing the way this works for now to just use string
     *              <code>$this->to = StringHelper::toCharArray($to);</code>
     */
    public function setTo($to)
    {
        $this->to = $to;
    }

    /**
     * {@inheritdoc}
     *
     * @param mixed $sourceFileName
     *
     * @return array|null
     */
    public function main($sourceFileName)
    {
        if ($this->reg === null || $this->to === null || !$this->reg->matches((string) $sourceFileName)) {
            return null;
        }

        return array($this->replaceReferences($sourceFileName));
    }

    /**
     * Replace all backreferences in the to pattern with the matched groups.
     * groups of the source.
     *
     * @param string $source The source filename.
     *
     * @return array|null|string
     *
     * FIXME Can't we just use engine->replace() to handle this?  the Preg engine will automatically convert \1 references to $1
     *
     * @intern the expression has already been processed (when ->matches() was run in Main())
     *         so no need to pass $source again to the engine.
     *         Replaces \1 with value of reg->getGroup(1) and return the modified "to" string.
     */
    private function replaceReferences($source)
    {
        return preg_replace_callback('/\\\([\d]+)/', array($this, 'replaceReferencesCallback'), $this->to);
    }

    /**
     * Gets the matched group from the Regexp engine.
     *
     * @param array $matches Matched elements.
     *
     * @return string
     */
    private function replaceReferencesCallback($matches)
    {
        return (string) $this->reg->getGroup($matches[1]);
    }
}
<?php

/*
 * $Id: f9449e6f47abcdaf727d3e42eeac9ab1c43998a2 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/parser/ExpatParseException.php';

/**
 * This is an abstract class all SAX handler classes must extend
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: f9449e6f47abcdaf727d3e42eeac9ab1c43998a2 $
 * @package   phing.parser
 */
abstract class AbstractHandler
{

    public $parentHandler = null;
    public $parser = null;

    /**
     * Constructs a SAX handler parser.
     *
     * The constructor must be called by all derived classes.
     *
     * @param   object  the parser object
     * @param   object  the parent handler of this handler
     */
    protected function __construct($parser, $parentHandler)
    {
        $this->parentHandler = $parentHandler;
        $this->parser = $parser;
        $this->parser->setHandler($this);
    }

    /**
     * Gets invoked when a XML open tag occurs
     *
     * Must be overloaded by the child class. Throws an ExpatParseException
     * if there is no handler registered for an element.
     *
     * @param $name
     * @param $attribs
     * @throws ExpatParseException
     * @internal param the $string name of the XML element
     * @internal param the $array attributes of the XML element
     */
    public function startElement($name, $attribs)
    {
        throw new ExpatParseException("Unexpected element $name");
    }

    /**
     * Gets invoked when element closes method.
     *
     */
    protected function finished()
    {
    }

    /**
     * Gets invoked when a XML element ends.
     *
     * Can be overloaded by the child class. But should not. It hands
     * over control to the parentHandler of this.
     *
     * @param  string  the name of the XML element
     */
    public function endElement($name)
    {
        $this->finished();
        $this->parser->setHandler($this->parentHandler);
    }

    /**
     * Invoked by occurrence of #PCDATA.
     *
     * @param     string  the name of the XML element
     * @throws ExpatParseException
     * @exception ExpatParserException if there is no CDATA but method
     *            was called
     */
    public function characters($data)
    {
        $s = trim($data);
        if (strlen($s) > 0) {
            throw new ExpatParseException("Unexpected text '$s'", $this->parser->getLocation());
        }
    }
}
<?php
/*
 *  $Id: e6378f0fcd291f9a2ccb23f6c82aeda662c9492b $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * The abstract SAX parser class.
 *
 * This class represents a SAX parser. It is a abstract calss that must be
 * implemented by the real parser that must extend this class
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @author    Hans Lellelid <hans@xmpl.org>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: e6378f0fcd291f9a2ccb23f6c82aeda662c9492b $
 * @package   phing.parser
 */
abstract class AbstractSAXParser
{

    /** The AbstractHandler object. */
    protected $handler;

    /**
     * Constructs a SAX parser
     */
    public function __construct()
    {
    }

    /**
     * Sets options for PHP interal parser. Must be implemented by the parser
     * class if it should be used.
     * @param $opt
     * @param $val
     * @return
     */
    abstract public function parserSetOption($opt, $val);

    /**
     * Sets the current element handler object for this parser. Usually this
     * is an object using extending "AbstractHandler".
     *
     * @param AbstractHandler $obj The handler object.
     */
    public function setHandler($obj)
    {
        $this->handler = $obj;
    }

    /**
     * Method that gets invoked when the parser runs over a XML start element.
     *
     * This method is called by PHP's internal parser functions and registered
     * in the actual parser implementation.
     * It gives control to the current active handler object by calling the
     * <code>startElement()</code> method.
     *
     * @param  object  the php's internal parser handle
     * @param  string  the open tag name
     * @param  array   the tag's attributes if any
     * @throws Exception - Exceptions may be thrown by the Handler
     */
    public function startElement($parser, $name, $attribs)
    {
        $this->handler->startElement($name, $attribs);
    }

    /**
     * Method that gets invoked when the parser runs over a XML close element.
     *
     * This method is called by PHP's internal parser funcitons and registered
     * in the actual parser implementation.
     *
     * It gives control to the current active handler object by calling the
     * <code>endElement()</code> method.
     *
     * @param   object  the php's internal parser handle
     * @param   string  the closing tag name
     * @throws Exception - Exceptions may be thrown by the Handler
     */
    public function endElement($parser, $name)
    {
        $this->handler->endElement($name);
    }

    /**
     * Method that gets invoked when the parser runs over CDATA.
     *
     * This method is called by PHP's internal parser functions and registered
     * in the actual parser implementation.
     *
     * It gives control to the current active handler object by calling the
     * <code>characters()</code> method. That processes the given CDATA.
     *
     * @param  resource  $parser php's internal parser handle.
     * @param  string    $data   the CDATA
     * @throws Exception - Exceptions may be thrown by the Handler
     */
    public function characters($parser, $data)
    {
        $this->handler->characters($data);
    }

    /**
     * Entrypoint for parser. This method needs to be implemented by the
     * child classt that utilizes the concrete parser
     */
    abstract public function parse();
}
<?php

/**
 *  $Id: cf8a5bbff30a758d6428a24861a1a532435eddad $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Interface for elements that want to be able to create custom child elements
 * at runtime.
 *
 * @author keith.rogers@unit4.com
 * @package phing.parser
 */
interface CustomChildCreator
{
    /**
     * Creates the object for the child element
     *
     * @param  string  $elementName the name of the element that has been requested
     * @param  Project $project     The project the element is in
     * @return object  Returns the nested element
     */
    public function customChildCreator($elementName, Project $project);
}
<?php
/*
 *  $Id: b8f555762a9eb7b012fb7712a208979dc18f82ab $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/RuntimeConfigurable.php';

/**
 * Configures a Project (complete with Targets and Tasks) based on
 * a XML build file.
 * <p>
 * Design/ZE2 migration note:
 * If PHP would support nested classes. All the phing/parser/*Filter
 * classes would be nested within this class
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: b8f555762a9eb7b012fb7712a208979dc18f82ab $
 * @package   phing.parser
 */

class DataTypeHandler extends AbstractHandler
{

    private $target;
    private $element;
    private $wrapper;

    /**
     * Constructs a new DataTypeHandler and sets up everything.
     *
     * @param AbstractSAXParser   $parser        The XML parser (default: ExpatParser)
     * @param AbstractHandler     $parentHandler The parent handler that invoked this handler.
     * @param ProjectConfigurator $configurator  The ProjectConfigurator object
     * @param Target              $target        The target object this datatype is contained in (null for top-level datatypes).
     */
    public function __construct(
        AbstractSAXParser $parser,
        AbstractHandler $parentHandler,
        ProjectConfigurator $configurator,
        $target = null
    ) { // FIXME b2 typehinting
        parent::__construct($parser, $parentHandler);
        $this->target = $target;
        $this->configurator = $configurator;
    }

    /**
     * Executes initialization actions required to setup the data structures
     * related to the tag.
     * <p>
     * This includes:
     * <ul>
     * <li>creation of the datatype object</li>
     * <li>calling the setters for attributes</li>
     * <li>adding the type to the target object if any</li>
     * <li>adding a reference to the task (if id attribute is given)</li>
     * </ul>
     *
     * @param  string  the tag that comes in
     * @param  array   attributes the tag carries
     * @throws ExpatParseException if attributes are incomplete or invalid
     */
    public function init($propType, $attrs)
    {
        // shorthands
        $project = $this->configurator->project;
        $configurator = $this->configurator;

        try { //try
            $this->element = $project->createDataType($propType);

            if ($this->element === null) {
                throw new BuildException("Unknown data type $propType");
            }

            if ($this->target !== null) {
                $this->wrapper = new RuntimeConfigurable($this->element, $propType);
                $this->wrapper->setAttributes($attrs);
                $this->target->addDataType($this->wrapper);
            } else {
                $configurator->configure($this->element, $attrs, $project);
                $configurator->configureId($this->element, $attrs);
            }

        } catch (BuildException $exc) {
            throw new ExpatParseException($exc, $this->parser->getLocation());
        }
    }

    /**
     * Handles character data.
     *
     * @param $data
     * @throws ExpatParseException
     * @internal param the $string CDATA that comes in
     */
    public function characters($data)
    {
        $project = $this->configurator->project;
        try { //try
            $this->configurator->addText($project, $this->element, $data);
        } catch (BuildException $exc) {
            throw new ExpatParseException($exc->getMessage(), $this->parser->getLocation());
        }
    }

    /**
     * Checks for nested tags within the current one. Creates and calls
     * handlers respectively.
     *
     * @param  string  the tag that comes in
     * @param  array   attributes the tag carries
     */
    public function startElement($name, $attrs)
    {
        $nef = new NestedElementHandler($this->parser, $this, $this->configurator, $this->element, $this->wrapper, $this->target);
        $nef->init($name, $attrs);
    }

    /**
     * Overrides endElement for data types. Tells the type
     * handler that processing the element had been finished so
     * handlers know they can perform actions that need to be
     * based on the data contained within the element.
     *
     * @param  string  the name of the XML element
     * @return void
     */
    public function endElement($name)
    {
        $this->element->parsingComplete();
        parent::endElement($name);
    }

}
<?php
/*
 *  $Id: cf184abc517b45b04db2dd95827cd0e3998b0712 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/parser/AbstractHandler.php';
require_once 'phing/UnknownElement.php';

/**
 * The generic element handler class.
 *
 * This class handles the occurrence of runtime registered tags like
 * datatypes (fileset, patternset, etc) and it's possible nested tags. It
 * introspects the implementation of the class and sets up the data structures.
 *
 * @author    Michiel Rook <mrook@php.net>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: cf184abc517b45b04db2dd95827cd0e3998b0712 $
 * @package   phing.parser
 */
class ElementHandler extends AbstractHandler
{

    /**
     * Reference to the parent object that represents the parent tag
     * of this nested element
     * @var object
     */
    private $parent;

    /**
     * Reference to the child object that represents the child tag
     * of this nested element
     * @var object
     */
    private $child;

    /**
     *  Reference to the parent wrapper object
     * @var object
     */
    private $parentWrapper;

    /**
     *  Reference to the child wrapper object
     * @var object
     */
    private $childWrapper;

    /**
     *  Reference to the related target object
     * @var object the target instance
     */
    private $target;

    /**
     *  Constructs a new NestedElement handler and sets up everything.
     *
     * @param  object  the ExpatParser object
     * @param  object  the parent handler that invoked this handler
     * @param  object  the ProjectConfigurator object
     * @param  object  the parent object this element is contained in
     * @param  object  the parent wrapper object
     * @param  object  the target object this task is contained in
     */
    public function __construct(
        $parser,
        $parentHandler,
        $configurator,
        $parent = null,
        $parentWrapper = null,
        $target = null
    ) {
        parent::__construct($parser, $parentHandler);
        $this->configurator = $configurator;
        if ($parentWrapper != null) {
            $this->parent = $parentWrapper->getProxy();
        } else {
            $this->parent = $parent;
        }
        $this->parentWrapper = $parentWrapper;
        $this->target = $target;
    }

    /**
     * Executes initialization actions required to setup the data structures
     * related to the tag.
     * <p>
     * This includes:
     * <ul>
     * <li>creation of the nested element</li>
     * <li>calling the setters for attributes</li>
     * <li>adding the element to the container object</li>
     * <li>adding a reference to the element (if id attribute is given)</li>
     * </ul>
     *
     * @param  string  the tag that comes in
     * @param  array   attributes the tag carries
     * @throws ExpatParseException if the setup process fails
     */
    public function init($propType, $attrs)
    {
        $configurator = $this->configurator;
        $project = $this->configurator->project;

        try {
            $this->child = new UnknownElement(strtolower($propType));
            $this->child->setTaskName($propType);
            $this->child->setTaskType($propType);
            $this->child->setProject($project);
            $this->child->setLocation($this->parser->getLocation());

            if ($this->target !== null) {
                $this->child->setOwningTarget($this->target);
            }

            if ($this->parent !== null) {
                $this->parent->addChild($this->child);
            } elseif ($this->target !== null) {
                $this->target->addTask($this->child);
            }

            $configurator->configureId($this->child, $attrs);

            $this->childWrapper = new RuntimeConfigurable($this->child, $propType);
            $this->childWrapper->setAttributes($attrs);

            if ($this->parentWrapper !== null) {
                $this->parentWrapper->addChild($this->childWrapper);
            }
        } catch (BuildException $exc) {
            throw new ExpatParseException("Error initializing nested element <$propType>", $exc, $this->parser->getLocation(
            ));
        }
    }

    /**
     * Handles character data.
     *
     * @param  string  the CDATA that comes in
     * @throws ExpatParseException if the CDATA could not be set-up properly
     */
    public function characters($data)
    {
        $configurator = $this->configurator;
        $project = $this->configurator->project;

        $this->childWrapper->addText($data);
    }

    /**
     * Checks for nested tags within the current one. Creates and calls
     * handlers respectively.
     *
     * @param  string  the tag that comes in
     * @param  array   attributes the tag carries
     */
    public function startElement($name, $attrs)
    {
        $eh = new ElementHandler($this->parser, $this, $this->configurator, $this->child, $this->childWrapper, $this->target);
        $eh->init($name, $attrs);
    }
}
<?php
/*
 *  $Id: 0421e1fce5039a2e55c1e35273382691c5830a48 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/BuildException.php';

/**
 * This class throws errors for Expat, the XML processor.
 *
 * @author   Andreas Aderhold, andi@binarycloud.com
 * @version  $Id: 0421e1fce5039a2e55c1e35273382691c5830a48 $
 * @package  phing.parser
 */
class ExpatParseException extends BuildException
{
}
<?php
/*
 *  $Id: 0e78856d8464d655550baa5a20b235b4049625f8 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/parser/AbstractSAXParser.php';
include_once 'phing/parser/ExpatParseException.php';
include_once 'phing/system/io/IOException.php';
include_once 'phing/system/io/FileReader.php';

/**
 * This class is a wrapper for the PHP's internal expat parser.
 *
 * It takes an XML file represented by a abstract path name, and starts
 * parsing the file and calling the different "trap" methods inherited from
 * the AbstractParser class.
 *
 * Those methods then invoke the represenatative methods in the registered
 * handler classes.
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: 0e78856d8464d655550baa5a20b235b4049625f8 $
 * @package   phing.parser
 */

class ExpatParser extends AbstractSAXParser
{

    /** @var resource */
    private $parser;

    /** @var Reader */
    private $reader;

    private $file;

    private $buffer = 4096;

    private $error_string = "";

    private $line = 0;

    /** @var Location Current cursor pos in XML file. */
    private $location;

    /**
     * Constructs a new ExpatParser object.
     *
     * The constructor accepts a PhingFile object that represents the filename
     * for the file to be parsed. It sets up php's internal expat parser
     * and options.
     *
     * @param  Reader    $reader   The Reader Object that is to be read from.
     * @param  string    $filename Filename to read.
     * @throws Exception if the given argument is not a PhingFile object
     */
    public function __construct(Reader $reader, $filename = null)
    {

        $this->reader = $reader;
        if ($filename !== null) {
            $this->file = new PhingFile($filename);
        }
        $this->parser = xml_parser_create();
        $this->buffer = 4096;
        $this->location = new Location();
        xml_set_object($this->parser, $this);
        xml_set_element_handler($this->parser, array($this, "startElement"), array($this, "endElement"));
        xml_set_character_data_handler($this->parser, array($this, "characters"));
    }

    /**
     * Override PHP's parser default settings, created in the constructor.
     *
     * @param $opt
     * @param $val
     * @internal param the $string option to set
     * @return boolean true if the option could be set, otherwise false
     */
    public function parserSetOption($opt, $val)
    {
        return xml_parser_set_option($this->parser, $opt, $val);
    }

    /**
     * Returns the location object of the current parsed element. It describes
     * the location of the element within the XML file (line, char)
     *
     * @return object the location of the current parser
     */
    public function getLocation()
    {
        if ($this->file !== null) {
            $path = $this->file->getAbsolutePath();
        } else {
            $path = $this->reader->getResource();
        }
        $this->location = new Location($path, xml_get_current_line_number($this->parser), xml_get_current_column_number(
            $this->parser
        ));

        return $this->location;
    }

    /**
     * Starts the parsing process.
     *
     * @param  string  the option to set
     * @return int                 1 if the parsing succeeded
     * @throws ExpatParseException if something gone wrong during parsing
     * @throws IOException         if XML file can not be accessed
     */
    public function parse()
    {

        while (($data = $this->reader->read()) !== -1) {
            if (!xml_parse($this->parser, $data, $this->reader->eof())) {
                $error = xml_error_string(xml_get_error_code($this->parser));
                $e = new ExpatParseException($error, $this->getLocation());
                xml_parser_free($this->parser);
                throw $e;
            }
        }
        xml_parser_free($this->parser);

        return 1;
    }
}
<?php
/*
 *  $Id: feed38557f156bd1c61593a048f37c2e246faf87 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Stores the file name and line number of a XML file
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: feed38557f156bd1c61593a048f37c2e246faf87 $
 * @package   phing.parser
 */

class Location
{

    private $fileName;
    private $lineNumber;
    private $columnNumber;

    /**
     * Constructs the location consisting of a file name and line number
     *
     * @param  string  the filename
     * @param  integer the line number
     * @param  integer the column number
     */
    public function __construct($fileName = null, $lineNumber = null, $columnNumber = null)
    {
        $this->fileName = $fileName;
        $this->lineNumber = $lineNumber;
        $this->columnNumber = $columnNumber;
    }

    /**
     * Returns the file name, line number and a trailing space.
     *
     * An error message can be appended easily. For unknown locations,
     * returns empty string.
     *
     * @return string the string representation of this Location object
     */
    public function toString()
    {
        $buf = "";
        if ($this->fileName !== null) {
            $buf .= $this->fileName;
            if ($this->lineNumber !== null) {
                $buf .= ":" . $this->lineNumber;
            }
            $buf .= ":" . $this->columnNumber;
        }

        return (string) $buf;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return $this->toString();
    }
}
<?php
/*
 * $Id: 077aa8989e2f94958a9e053a52a3eff7e58938b0 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Track the current state of the Xml parse operation.
 *
 * @author    Bryan Davis <bpd@keynetics.com>
 * @version   $Id: 077aa8989e2f94958a9e053a52a3eff7e58938b0 $
 * @package   phing.parser
 */
class PhingXMLContext
{

    /**
     * Target that will hold all tasks/types placed outside of targets
     *
     * @var Target
     */
    private $implicitTarget;

    /**
     * Current target
     *
     * @var Target
     */
    private $currentTarget = null;

    /**
     * List of current targets
     *
     * @var Target[]
     */
    private $currentTargets = null;

    /**
     * Constructor
     * @param Project $project the project to which this antxml context belongs to
     */
    public function __construct(Project $project)
    {
        $this->project = $project;
        $this->implicitTarget = new Target();
        $this->implicitTarget->setName("");
        $this->implicitTarget->setHidden(true);
    }

    /** The project to configure. */
    private $project;

    private $configurators = array();

    /**
     * @param $cfg
     */
    public function startConfigure($cfg)
    {
        $this->configurators[] = $cfg;
    }

    public function endConfigure()
    {
        array_pop($this->configurators);
    }

    /**
     * @return null
     */
    public function getConfigurator()
    {
        $l = count($this->configurators);
        if (0 == $l) {
            return null;
        } else {
            return $this->configurators[$l - 1];
        }
    }

    /** Impoerted files */
    private $importStack = array();

    /**
     * @param $file
     */
    public function addImport($file)
    {
        $this->importStack[] = $file;
    }

    /**
     * @return array
     */
    public function getImportStack()
    {
        return $this->importStack;
    }

    /**
     * find out the project to which this context belongs
     * @return project
     */
    public function getProject()
    {
        return $this->project;
    }

    /**
     * @return Target
     */
    public function getImplicitTarget()
    {
        return $this->implicitTarget;
    }

    /**
     * @param Target $target
     */
    public function setImplicitTarget(Target $target)
    {
        $this->implicitTarget = $target;
    }

    /**
     * @return Target
     */
    public function getCurrentTarget()
    {
        return $this->currentTarget;
    }

    /**
     * @param Target $target
     */
    public function setCurrentTarget(Target $target)
    {
        $this->currentTarget = $target;
    }

    /**
     * @return Target[]
     */
    public function &getCurrentTargets()
    {
        return $this->currentTargets;
    }

    /**
     * @param Target[] $currentTargets
     */
    public function setCurrentTargets(array $currentTargets)
    {
        $this->currentTargets = $currentTargets;
    }

} //end PhingXMLContext
<?php
/*
 * $Id: 66d9b73c5215e885e60e99f716f1006d27df2ad2 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/system/io/BufferedReader.php';
include_once 'phing/system/io/FileReader.php';
include_once 'phing/BuildException.php';
include_once 'phing/system/lang/FileNotFoundException.php';
include_once 'phing/system/io/PhingFile.php';
include_once 'phing/parser/PhingXMLContext.php';
include_once 'phing/IntrospectionHelper.php';

/**
 * The datatype handler class.
 *
 * This class handles the occurrence of registered datatype tags like
 * FileSet
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: 66d9b73c5215e885e60e99f716f1006d27df2ad2 $
 * @package   phing.parser
 */
class ProjectConfigurator
{

    public $project;
    public $locator;

    public $buildFile;
    public $buildFileParent;

    /** Synthetic target that will be called at the end to the parse phase */
    private $parseEndTarget;

    /** Name of the current project */
    private $currentProjectName;

    private $isParsing = true;

    /**
     * Indicates whether the project tag attributes are to be ignored
     * when processing a particular build file.
     */
    private $ignoreProjectTag = false;

    /**
     * Static call to ProjectConfigurator. Use this to configure a
     * project. Do not use the new operator.
     *
     * @param  Project $project  the Project instance this configurator should use
     * @param  PhingFile $buildFile  the buildfile object the parser should use
     */
    public static function configureProject(Project $project, PhingFile $buildFile)
    {
        $pc = new ProjectConfigurator($project, $buildFile);
        $pc->parse();
    }

    /**
     * Constructs a new ProjectConfigurator object
     * This constructor is private. Use a static call to
     * <code>configureProject</code> to configure a project.
     *
     * @param  Project $project     the Project instance this configurator should use
     * @param  PhingFile $buildFile the buildfile object the parser should use
     */
    public function __construct(Project $project, PhingFile $buildFile)
    {
        $this->project = $project;
        $this->buildFile = new PhingFile($buildFile->getAbsolutePath());
        $this->buildFileParent = new PhingFile($this->buildFile->getParent());
        $this->parseEndTarget = new Target();
    }

    /**
     * find out the build file
     * @return PhingFile the build file to which the xml context belongs
     */
    public function getBuildFile()
    {
        return $this->buildFile;
    }

    /**
     * find out the parent build file of this build file
     * @return PhingFile the parent build file of this build file
     */
    public function getBuildFileParent()
    {
        return $this->buildFileParent;
    }

    /**
     * find out the current project name
     * @return string current project name
     */
    public function getCurrentProjectName()
    {
        return $this->currentProjectName;
    }

    /**
     * set the name of the current project
     * @param string $name name of the current project
     */
    public function setCurrentProjectName($name)
    {
        $this->currentProjectName = $name;
    }

    /**
     * tells whether the project tag is being ignored
     * @return bool whether the project tag is being ignored
     */
    public function isIgnoringProjectTag()
    {
        return $this->ignoreProjectTag;
    }

    /**
     * sets the flag to ignore the project tag
     * @param bool $flag flag to ignore the project tag
     */
    public function setIgnoreProjectTag($flag)
    {
        $this->ignoreProjectTag = $flag;
    }

    /**
     * @return bool
     */
    public function isParsing()
    {
        return $this->isParsing;
    }

    /**
     * Creates the ExpatParser, sets root handler and kick off parsing
     * process.
     *
     * @throws BuildException if there is any kind of exception during
     *                        the parsing process
     */
    protected function parse()
    {
        try {
            // get parse context
            $ctx = $this->project->getReference("phing.parsing.context");
            if (null == $ctx) {
                // make a new context and register it with project
                $ctx = new PhingXMLContext($this->project);
                $this->project->addReference("phing.parsing.context", $ctx);
            }

            //record this parse with context
            $ctx->addImport($this->buildFile);

            if (count($ctx->getImportStack()) > 1) {
                $currentImplicit = $ctx->getImplicitTarget();
                $currentTargets = $ctx->getCurrentTargets();

                $newCurrent = new Target();
                $newCurrent->setProject($this->project);
                $newCurrent->setName('');
                $ctx->setCurrentTargets(array());
                $ctx->setImplicitTarget($newCurrent);

                // this is an imported file
                // modify project tag parse behavior
                $this->setIgnoreProjectTag(true);
                $this->_parse($ctx);
                $newCurrent->main();

                $ctx->setImplicitTarget($currentImplicit);
                $ctx->setCurrentTargets($currentTargets);
            } else {
                $ctx->setCurrentTargets(array());
                $this->_parse($ctx);
                $ctx->getImplicitTarget()->main();
            }

        } catch (Exception $exc) {
            //throw new BuildException("Error reading project file", $exc);
            throw $exc;
        }
    }

    /**
     * @param PhingXMLContext $ctx
     * @throws ExpatParseException
     */
    protected function _parse(PhingXMLContext $ctx)
    {
        // push action onto global stack
        $ctx->startConfigure($this);

        $reader = new BufferedReader(new FileReader($this->buildFile));
        $parser = new ExpatParser($reader);
        $parser->parserSetOption(XML_OPTION_CASE_FOLDING, 0);
        $parser->setHandler(new RootHandler($parser, $this, $ctx));
        $this->project->log("parsing buildfile " . $this->buildFile->getName(), Project::MSG_VERBOSE);
        $parser->parse();
        $reader->close();

        // mark parse phase as completed
        $this->isParsing = false;
        // execute delayed tasks
        $this->parseEndTarget->main();
        // pop this action from the global stack
        $ctx->endConfigure();
    }

    /**
     * Delay execution of a task until after the current parse phase has
     * completed.
     *
     * @param Task $task Task to execute after parse
     */
    public function delayTaskUntilParseEnd($task)
    {
        $this->parseEndTarget->addTask($task);
    }

    /**
     * Configures an element and resolves eventually given properties.
     *
     * @param mixed $target element to configure
     * @param array $attrs element's attributes
     * @param Project $project project this element belongs to
     * @throws BuildException
     * @throws Exception
     */
    public static function configure($target, $attrs, Project $project)
    {

        if ($target instanceof TaskAdapter) {
            $target = $target->getProxy();
        }

        // if the target is an UnknownElement, this means that the tag had not been registered
        // when the enclosing element (task, target, etc.) was configured.  It is possible, however,
        // that the tag was registered (e.g. using <taskdef>) after the original configuration.
        // ... so, try to load it again:
        if ($target instanceof UnknownElement) {
            $tryTarget = $project->createTask($target->getTaskType());
            if ($tryTarget) {
                $target = $tryTarget;
            }
        }

        $bean = get_class($target);
        $ih = IntrospectionHelper::getHelper($bean);

        foreach ($attrs as $key => $value) {
            if ($key == 'id') {
                continue;
                // throw new BuildException("Id must be set Extermnally");
            }
            $value = self::replaceProperties($project, $value, $project->getProperties());
            try { // try to set the attribute
                $ih->setAttribute($project, $target, strtolower($key), $value);
            } catch (BuildException $be) {
                // id attribute must be set externally
                if ($key !== "id") {
                    throw $be;
                }
            }
        }
    }

    /**
     * Configures the #CDATA of an element.
     *
     * @param  object  the project this element belongs to
     * @param  object  the element to configure
     * @param  string  the element's #CDATA
     */
    public static function addText($project, $target, $text = null)
    {
        if ($text === null || strlen(trim($text)) === 0) {
            return;
        }
        $ih = IntrospectionHelper::getHelper(get_class($target));
        $text = self::replaceProperties($project, $text, $project->getProperties());
        $ih->addText($project, $target, $text);
    }

    /**
     * Stores a configured child element into its parent object
     *
     * @param  object  the project this element belongs to
     * @param  object  the parent element
     * @param  object  the child element
     * @param  string  the XML tagname
     */
    public static function storeChild($project, $parent, $child, $tag)
    {
        $ih = IntrospectionHelper::getHelper(get_class($parent));
        $ih->storeElement($project, $parent, $child, $tag);
    }

    // The following three properties are a sort of hack
    // to enable a static function to serve as the callback
    // for preg_replace_callback().  Clearly we cannot use object
    // variables, since the replaceProperties() is called statically.
    // This is IMO better than using global variables in the callback.

    private static $propReplaceProject;
    private static $propReplaceProperties;
    private static $propReplaceLogLevel = Project::MSG_VERBOSE;

    /**
     * Replace ${} style constructions in the given value with the
     * string value of the corresponding data types. This method is
     * static.
     *
     * @param object|Project $project the project that should be used for property look-ups
     * @param  string $value the string to be scanned for property references
     * @param  array $keys property keys
     * @param int $logLevel the level of generated log messages
     * @return string  the replaced string or <code>null</code> if the string
     *                          itself was null
     */
    public static function replaceProperties(Project $project, $value, $keys, $logLevel = Project::MSG_VERBOSE)
    {

        if ($value === null) {
            return null;
        }

        // These are a "hack" to support static callback for preg_replace_callback()

        // make sure these get initialized every time
        self::$propReplaceProperties = $keys;
        self::$propReplaceProject = $project;
        self::$propReplaceLogLevel = $logLevel;

        // Because we're not doing anything special (like multiple passes),
        // regex is the simplest / fastest.  PropertyTask, though, uses
        // the old parsePropertyString() method, since it has more stringent
        // requirements.

        $sb = $value;
        $iteration = 0;

        // loop to recursively replace tokens
        while (strpos($sb, '${') !== false) {
            $sb = preg_replace_callback(
                '/\$\{([^\$}]+)\}/',
                array('ProjectConfigurator', 'replacePropertyCallback'),
                $sb
            );

            // keep track of iterations so we can break out of otherwise infinite loops.
            $iteration++;
            if ($iteration == 5) {
                return $sb;
            }
        }

        return $sb;
    }

    /**
     * Private [static] function for use by preg_replace_callback to replace a single param.
     * This method makes use of a static variable to hold the
     * @param $matches
     * @return string
     */
    private static function replacePropertyCallback($matches)
    {
        $propertyName = $matches[1];
        if (!isset(self::$propReplaceProperties[$propertyName])) {
            self::$propReplaceProject->log(
                'Property ${' . $propertyName . '} has not been set.',
                self::$propReplaceLogLevel
            );

            return $matches[0];
        } else {
            self::$propReplaceProject->log(
                'Property ${' . $propertyName . '} => ' . self::$propReplaceProperties[$propertyName],
                self::$propReplaceLogLevel
            );
        }

        $propertyValue = self::$propReplaceProperties[$propertyName];

        if (is_bool($propertyValue)) {
            if ($propertyValue === true) {
                $propertyValue = "true";
            } else {
                $propertyValue = "false";
            }
        }

        return $propertyValue;
    }

    /**
     * Scan Attributes for the id attribute and maybe add a reference to
     * project.
     *
     * @param object the element's object
     * @param array  the element's attributes
     */
    public function configureId($target, $attr)
    {
        if (isset($attr['id']) && $attr['id'] !== null) {
            $this->project->addReference($attr['id'], $target);
        }
    }
}
<?php
/*
 * $Id: d0fb923d8e3582c8d9c75a59f19d0ba4ea6812f8 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/parser/AbstractHandler.php';
require_once 'phing/system/io/PhingFile.php';
require_once 'phing/parser/ElementHandler.php';

/**
 * Handler class for the <project> XML element This class handles all elements
 * under the <project> element.
 *
 * @author      Andreas Aderhold <andi@binarycloud.com>
 * @copyright (c) 2001,2002 THYRELL. All rights reserved
 * @version   $Id: d0fb923d8e3582c8d9c75a59f19d0ba4ea6812f8 $
 * @package   phing.parser
 */
class ProjectHandler extends AbstractHandler
{

    /**
     * The phing project configurator object.
     * @var ProjectConfigurator
     */
    private $configurator;

    /**
     * @var PhingXMLContext
     */
    private $context;

    /**
     * Constructs a new ProjectHandler
     *
     * @param  object  the ExpatParser object
     * @param  object  the parent handler that invoked this handler
     * @param  ProjectConfigurator $configurator the ProjectConfigurator object
     * @param PhingXMLContext $context
     */
    public function __construct($parser, $parentHandler, $configurator, PhingXMLContext $context)
    {
        parent::__construct($parser, $parentHandler);

        $this->configurator = $configurator;
        $this->context = $context;
    }

    /**
     * Executes initialization actions required to setup the project. Usually
     * this method handles the attributes of a tag.
     *
     * @param  string $tag the tag that comes in
     * @param  array  $attrs attributes the tag carries
     * @throws ExpatParseException if attributes are incomplete or invalid
     */
    public function init($tag, $attrs)
    {
        $def = null;
        $name = null;
        $id = null;
        $desc = null;
        $baseDir = null;
        $ver = null;

        // some shorthands
        $project = $this->configurator->project;
        $buildFileParent = $this->configurator->buildFileParent;

        foreach ($attrs as $key => $value) {
            if ($key === "default") {
                $def = $value;
            } elseif ($key === "name") {
                $name = $value;
            } elseif ($key === "id") {
                $id = $value;
            } elseif ($key === "basedir") {
                $baseDir = $value;
            } elseif ($key === "description") {
                $desc = $value;
            } elseif ($key === "phingVersion") {
                $ver = $value;
            } else {
                throw new ExpatParseException("Unexpected attribute '$key'");
            }
        }
        // these things get done no matter what
        if (null == $name) {
            $name = basename($this->configurator->getBuildFile());
        }

        $canonicalName = self::canonicalName($name);
        $this->configurator->setCurrentProjectName($canonicalName);
        $path = (string) $this->configurator->getBuildFile();
        $project->setUserProperty("phing.file.{$canonicalName}", $path);
        $project->setUserProperty("phing.dir.{$canonicalName}", dirname($path));

        if ($this->configurator->isIgnoringProjectTag()) {
            return;
        }

        if ($def === null) {
            throw new ExpatParseException(
                "The default attribute of project is required");
        }

        $project->setDefaultTarget($def);

        if ($name !== null) {
            $project->setName($name);
            $project->addReference($name, $project);
        }

        if ($id !== null) {
            $project->addReference($id, $project);
        }

        if ($desc !== null) {
            $project->setDescription($desc);
        }

        if ($ver !== null) {
            $project->setPhingVersion($ver);
        }

        if ($project->getProperty("project.basedir") !== null) {
            $project->setBasedir($project->getProperty("project.basedir"));
        } else {
            if ($baseDir === null) {
                $project->setBasedir($buildFileParent->getAbsolutePath());
            } else {
                // check whether the user has specified an absolute path
                $f = new PhingFile($baseDir);
                if ($f->isAbsolute()) {
                    $project->setBasedir($baseDir);
                } else {
                    $project->setBaseDir($project->resolveFile($baseDir, new PhingFile(getcwd())));
                }
            }
        }

        $project->addTarget("", $this->context->getImplicitTarget());
    }

    /**
     * Handles start elements within the <project> tag by creating and
     * calling the required handlers for the detected element.
     *
     * @param  string  the tag that comes in
     * @param  array   attributes the tag carries
     * @throws ExpatParseException if a unxepected element occurs
     */
    public function startElement($name, $attrs)
    {

        $project = $this->configurator->project;
        $types = $project->getDataTypeDefinitions();

        if ($name == "target") {
            $tf = new TargetHandler($this->parser, $this, $this->configurator, $this->context);
            $tf->init($name, $attrs);
        } else {
            $tf = new ElementHandler($this->parser, $this, $this->configurator, null, null, $this->context->getImplicitTarget(
            ));
            $tf->init($name, $attrs);
        }
    }

    /**
     * @param $name
     * @return mixed
     */
    public static function canonicalName($name)
    {
        return preg_replace('/\W/', '_', strtolower($name));
    }
}
<?php
/*
 *  $Id: 53455515ad7c75ce22073d8bfbc06779e2ec7e54 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/parser/AbstractHandler.php';
include_once 'phing/parser/ExpatParseException.php';
include_once 'phing/parser/ProjectHandler.php';

/**
 * Root filter class for a phing buildfile.
 *
 * The root filter is called by the parser first. This is where the phing
 * specific parsing starts. RootHandler decides what to do next.
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: 53455515ad7c75ce22073d8bfbc06779e2ec7e54 $
 * @package   phing.parser
 */
class RootHandler extends AbstractHandler
{

    /**
     * The phing project configurator object
     */
    private $configurator;

    /**
     * @var PhingXMLContext
     */
    private $context;

    /**
     * Constructs a new RootHandler
     *
     * The root filter is required so the parser knows what to do. It's
     * called by the ExpatParser that is instatiated in ProjectConfigurator.
     *
     * It receives the expat parse object ref and a reference to the
     * configurator
     *
     * @param AbstractSAXParser $parser The ExpatParser object.
     * @param ProjectConfigurator $configurator The ProjectConfigurator object.
     * @param PhingXMLContext $context
     */
    public function __construct(AbstractSAXParser $parser, ProjectConfigurator $configurator, PhingXMLContext $context)
    {
        $this->configurator = $configurator;
        $this->context = $context;

        parent::__construct($parser, $this);
    }

    /**
     * Kick off a custom action for a start element tag.
     *
     * The root element of our buildfile is the &lt;project&gt; element. The
     * root filter handles this element if it occurs, creates ProjectHandler
     * to handle any nested tags & attributes of the &lt;project&gt; tag,
     * and calls init.
     *
     * @param  string              $tag   The xml tagname
     * @param  array               $attrs The attributes of the tag
     * @throws ExpatParseException if the first element within our build file
     *                                   is not the &gt;project&lt; element
     */
    public function startElement($tag, $attrs)
    {
        if ($tag === "project") {
            $ph = new ProjectHandler($this->parser, $this, $this->configurator, $this->context);
            $ph->init($tag, $attrs);
        } else {
            throw new ExpatParseException("Unexpected tag <$tag> in top-level of build file.", $this->parser->getLocation(
            ));
        }
    }
}
<?php
/*
 * $Id: f9ccd55f0ac19fc1deb608d98eda7a05d53852c5 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/parser/AbstractHandler.php';

/**
 * The target handler class.
 *
 * This class handles the occurrence of a <target> tag and it's possible
 * nested tags (datatypes and tasks).
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright  2001,2002 THYRELL. All rights reserved
 * @version   $Id: f9ccd55f0ac19fc1deb608d98eda7a05d53852c5 $
 * @package   phing.parser
 */
class TargetHandler extends AbstractHandler
{

    /**
     * Reference to the target object that represents the currently parsed
     * target.
     * @var object the target instance
     */
    private $target;

    /**
     * The phing project configurator object
     * @var ProjectConfigurator
     */
    private $configurator;

    /**
     * Constructs a new TargetHandler
     *
     * @param AbstractSAXParser $parser
     * @param AbstractHandler $parentHandler
     * @param ProjectConfigurator $configurator
     * @param PhingXMLContext $context
     * @internal param the $object ExpatParser object
     * @internal param the $object parent handler that invoked this handler
     * @internal param the $object ProjectConfigurator object
     */
    public function __construct(
        AbstractSAXParser $parser,
        AbstractHandler $parentHandler,
        ProjectConfigurator $configurator,
        PhingXMLContext $context
    ) {
        parent::__construct($parser, $parentHandler);
        $this->configurator = $configurator;
        $this->context = $context;
    }

    /**
     * Executes initialization actions required to setup the data structures
     * related to the tag.
     * <p>
     * This includes:
     * <ul>
     * <li>creation of the target object</li>
     * <li>calling the setters for attributes</li>
     * <li>adding the target to the project</li>
     * <li>adding a reference to the target (if id attribute is given)</li>
     * </ul>
     *
     * @param $tag
     * @param $attrs
     * @throws BuildException
     * @throws ExpatParseException
     * @internal param the $string tag that comes in
     * @internal param attributes $array the tag carries
     */
    public function init($tag, $attrs)
    {
        $name = null;
        $depends = "";
        $ifCond = null;
        $unlessCond = null;
        $id = null;
        $description = null;
        $isHidden = false;
        $logskipped = false;

        foreach ($attrs as $key => $value) {
            switch ($key) {
                case "name":
                    $name = (string) $value;
                    break;
                case "depends":
                    $depends = (string) $value;
                    break;
                case "if":
                    $ifCond = (string) $value;
                    break;
                case "unless":
                    $unlessCond = (string) $value;
                    break;
                case "id":
                    $id = (string) $value;
                    break;
                case "hidden":
                    $isHidden = ($value == 'true' || $value == '1') ? true : false;
                    break;
                case "description":
                    $description = (string) $value;
                    break;
                case "logskipped":
                    $logskipped = $value;
                    break;
                default:
                    throw new ExpatParseException("Unexpected attribute '$key'", $this->parser->getLocation());
            }
        }

        if ($name === null) {
            throw new ExpatParseException("target element appears without a name attribute", $this->parser->getLocation(
            ));
        }

        // shorthand
        $project = $this->configurator->project;

        // check to see if this target is a dup within the same file
        if (isset($this->context->getCurrentTargets[$name])) {
            throw new BuildException("Duplicate target: $name",
                $this->parser->getLocation());
        }

        $this->target = new Target();
        $this->target->setHidden($isHidden);
        $this->target->setIf($ifCond);
        $this->target->setUnless($unlessCond);
        $this->target->setDescription($description);
        $this->target->setLogSkipped(StringHelper::booleanValue($logskipped));
        // take care of dependencies
        if (strlen($depends) > 0) {
            $this->target->setDepends($depends);
        }

        // check to see if target with same name is already defined
        $projectTargets = $project->getTargets();
        if (isset($projectTargets[$name])) {
            if ($this->configurator->isIgnoringProjectTag() &&
                $this->configurator->getCurrentProjectName() != null &&
                strlen($this->configurator->getCurrentProjectName()) != 0
            ) {
                // In an impored file (and not completely
                // ignoring the project tag)
                $newName = $this->configurator->getCurrentProjectName() . "." . $name;
                $project->log(
                    "Already defined in main or a previous import, " .
                    "define {$name} as {$newName}",
                    Project::MSG_VERBOSE
                );
                $name = $newName;
            } else {
                $project->log(
                    "Already defined in main or a previous import, " .
                    "ignore {$name}",
                    Project::MSG_VERBOSE
                );
                $name = null;
            }
        }

        if ($name != null) {
            $this->target->setName($name);
            $project->addOrReplaceTarget($name, $this->target);
            if ($id !== null && $id !== "") {
                $project->addReference($id, $this->target);
            }
        }
    }

    /**
     * Checks for nested tags within the current one. Creates and calls
     * handlers respectively.
     *
     * @param  string  the tag that comes in
     * @param  array   attributes the tag carries
     */
    public function startElement($name, $attrs)
    {
        // shorthands
        $project = $this->configurator->project;
        $types = $project->getDataTypeDefinitions();

        $tmp = new ElementHandler($this->parser, $this, $this->configurator, null, null, $this->target);
        $tmp->init($name, $attrs);
    }

    /**
     * Checks if this target has dependencies and/or nested tasks.
     * If the target has neither, show a warning.
     */
    protected function finished()
    {
        if (!count($this->target->getDependencies()) && !count($this->target->getTasks())) {
            $this->configurator->project->log(
                "Warning: target '" . $this->target->getName() .
                "' has no tasks or dependencies",
                Project::MSG_WARN
            );
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Diagnostics.php';
require_once 'phing/Project.php';
require_once 'phing/ProjectComponent.php';
require_once 'phing/Target.php';
require_once 'phing/Task.php';

include_once 'phing/BuildException.php';
include_once 'phing/ConfigurationException.php';
include_once 'phing/BuildEvent.php';

include_once 'phing/parser/Location.php';
include_once 'phing/parser/ExpatParser.php';
include_once 'phing/parser/AbstractHandler.php';
include_once 'phing/parser/ProjectConfigurator.php';
include_once 'phing/parser/RootHandler.php';
include_once 'phing/parser/ProjectHandler.php';
include_once 'phing/parser/TargetHandler.php';
include_once 'phing/parser/DataTypeHandler.php';

include_once 'phing/system/util/Properties.php';
include_once 'phing/util/StringHelper.php';
include_once 'phing/system/io/PhingFile.php';
include_once 'phing/system/io/OutputStream.php';
include_once 'phing/system/io/PrintStream.php';
include_once 'phing/system/io/FileOutputStream.php';
include_once 'phing/system/io/FileParserFactory.php';
include_once 'phing/system/io/FileReader.php';
include_once 'phing/system/util/Register.php';

/**
 * Entry point into Phing.  This class handles the full lifecycle of a build -- from
 * parsing & handling commandline arguments to assembling the project to shutting down
 * and cleaning up in the end.
 *
 * If you are invoking Phing from an external application, this is still
 * the class to use.  Your application can invoke the start() method, passing
 * any commandline arguments or additional properties.
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @author    Hans Lellelid <hans@xmpl.org>
 *
 * @package   phing
 */
class Phing
{
    /** Alias for phar file */
    const PHAR_ALIAS = 'phing.phar';

    /** The default build file name */
    const DEFAULT_BUILD_FILENAME = "build.xml";
    const PHING_HOME = 'phing.home';
    const PHP_VERSION = 'php.version';
    const PHP_INTERPRETER = 'php.interpreter';

    /** Our current message output status. Follows Project::MSG_XXX */
    private static $msgOutputLevel = Project::MSG_INFO;

    /** PhingFile that we are using for configuration */
    private $buildFile = null;

    /** The build targets */
    private $targets = array();

    /**
     * Set of properties that are passed in from commandline or invoking code.
     * @var Properties
     */
    private static $definedProps;

    /** Names of classes to add as listeners to project */
    private $listeners = array();

    /**
     * keep going mode
     * @var bool $keepGoingMode
     */
    private $keepGoingMode = false;

    private $loggerClassname = null;

    /** The class to handle input (can be only one). */
    private $inputHandlerClassname;

    /**
     * Whether or not log output should be reduced to the minimum.
     *
     * @var bool $silent
     */
    private $silent = false;

    /** Indicates if this phing should be run */
    private $readyToRun = false;

    /** Indicates we should only parse and display the project help information */
    private $projectHelp = false;

    /** Used by utility function getResourcePath() */
    private static $importPaths;

    /** System-wide static properties (moved from System) */
    private static $properties = array();

    /** Static system timer. */
    private static $timer;

    /** The current Project */
    private static $currentProject;

    /** Whether to capture PHP errors to buffer. */
    private static $phpErrorCapture = false;

    /** Whether to values in a property file should override existing values. */
    private $propertyFileOverride = false;

    /** Array of captured PHP errors */
    private static $capturedPhpErrors = array();

    /**
     * @var OUtputStream Stream for standard output.
     */
    private static $out;

    /**
     * @var OutputStream Stream for error output.
     */
    private static $err;

    /**
     * @var boolean Whether we are using a logfile.
     */
    private static $isLogFileUsed = false;

    /**
     * Array to hold original ini settings that Phing changes (and needs
     * to restore in restoreIni() method).
     *
     * @var array Struct of array(setting-name => setting-value)
     * @see restoreIni()
     */
    private static $origIniSettings = array();

    /** Whether or not output to the log is to be unadorned. */
    private $emacsMode = false;

    /**
     * Entry point allowing for more options from other front ends.
     *
     * This method encapsulates the complete build lifecycle.
     *
     * @param  array     $args                     The commandline args passed to phing shell script.
     * @param  array     $additionalUserProperties Any additional properties to be passed to Phing (alternative front-end might implement this).
     *                                             These additional properties will be available using the getDefinedProperty() method and will
     *                                             be added to the project's "user" properties
     * @see execute()
     * @see runBuild()
     * @throws Exception - if there is an error during build
     */
    public static function start($args, array $additionalUserProperties = null)
    {

        try {
            $m = new Phing();
            $m->execute($args);
        } catch (Exception $exc) {
            self::handleLogfile();
            self::printMessage($exc);
            self::statusExit(1);
            return;
        }

        if ($additionalUserProperties !== null) {
            foreach ($additionalUserProperties as $key => $value) {
                $m->setDefinedProperty($key, $value);
            }
        }

        // expect the worst
        $exitCode = 1;
        try {
            try {
                $m->runBuild();
                $exitCode = 0;
            } catch (ExitStatusException $ese) {
                $exitCode = $ese->getCode();
                if ($exitCode != 0) {
                    self::handleLogfile();
                    throw $ese;
                }
            }
        } catch (BuildException $exc) {
            // avoid printing output twice: self::printMessage($exc);
        } catch (Exception $exc) {
             echo $exc->getTraceAsString();
             self::printMessage($exc);
         }

        // everything fine, shutdown
        self::handleLogfile();
        self::statusExit($exitCode);
    }

    /**
     * This operation is expected to call `exit($int)`, which
     * is what the base version does.
     * However, it is possible to do something else.
     * @param int $exitCode code to exit with
     */
    protected static function statusExit($exitCode)
    {
        exit($exitCode);
    }

    /**
     * Prints the message of the Exception if it's not null.
     * @param Exception $t
     */
    public static function printMessage(Exception $t)
    {
        if (self::$err === null) { // Make sure our error output is initialized
            self::initializeOutputStreams();
        }
        if (self::getMsgOutputLevel() >= Project::MSG_VERBOSE) {
            self::$err->write($t->__toString() . PHP_EOL);
        } else {
            self::$err->write($t->getMessage() . PHP_EOL);
        }
    }

    /**
     * Sets the stdout and stderr streams if they are not already set.
     */
    private static function initializeOutputStreams()
    {
        if (self::$out === null) {
            if (!defined('STDOUT')) {
                self::$out = new OutputStream(fopen('php://stdout', 'w'));
            } else {
                self::$out = new OutputStream(STDOUT);
            }
        }
        if (self::$err === null) {
            if (!defined('STDERR')) {
                self::$err = new OutputStream(fopen('php://stderr', 'w'));
            } else {
                self::$err = new OutputStream(STDERR);
            }
        }
    }

    /**
     * Sets the stream to use for standard (non-error) output.
     *
     * @param OutputStream $stream The stream to use for standard output.
     */
    public static function setOutputStream(OutputStream $stream)
    {
        self::$out = $stream;
    }

    /**
     * Gets the stream to use for standard (non-error) output.
     *
     * @return OutputStream
     */
    public static function getOutputStream()
    {
        return self::$out;
    }

    /**
     * Sets the stream to use for error output.
     *
     * @param OutputStream $stream The stream to use for error output.
     */
    public static function setErrorStream(OutputStream $stream)
    {
        self::$err = $stream;
    }

    /**
     * Gets the stream to use for error output.
     *
     * @return OutputStream
     */
    public static function getErrorStream()
    {
        return self::$err;
    }

    /**
     * Close logfiles, if we have been writing to them.
     *
     * @since Phing 2.3.0
     *
     * @return void
     */
    private static function handleLogfile()
    {
        if (self::$isLogFileUsed) {
            self::$err->close();
            self::$out->close();
        }
    }

    /**
     * Making output level a static property so that this property
     * can be accessed by other parts of the system, enabling
     * us to display more information -- e.g. backtraces -- for "debug" level.
     *
     * @return int
     */
    public static function getMsgOutputLevel()
    {
        return self::$msgOutputLevel;
    }

    /**
     * Command line entry point. This method kicks off the building
     * of a project object and executes a build using either a given
     * target or the default target.
     *
     * @param  array $args Command line args.
     *
     * @return void
     */
    public static function fire($args)
    {
        self::start($args, null);
    }

    /**
     * Setup/initialize Phing environment from commandline args.
     * @param  array $args commandline args passed to phing shell.
     *
     * @throws ConfigurationException
     *
     * @return void
     */
    public function execute($args)
    {

        self::$definedProps = new Properties();
        $this->searchForThis = null;

        // 1) First handle any options which should always
        // Note: The order in which these are executed is important (if multiple of these options are specified)

        if (in_array('-help', $args) || in_array('-h', $args)) {
            $this->printUsage();

            return;
        }

        if (in_array('-version', $args) || in_array('-v', $args)) {
            $this->printVersion();

            return;
        }

        if (in_array('-diagnostics', $args)) {
            Diagnostics::doReport(new PrintStream(self::$out));

            return;
        }

        // 2) Next pull out stand-alone args.
        // Note: The order in which these are executed is important (if multiple of these options are specified)

        if (false !== ($key = array_search('-quiet', $args, true)) || false !== ($key = array_search(
                '-q',
                $args,
                true
            ))
        ) {
            self::$msgOutputLevel = Project::MSG_WARN;
            unset($args[$key]);
        }

        if (
            false !== ($key = array_search('-emacs', $args, true))
            ||
            false !== ($key = array_search('-e', $args, true))
        ) {
            $this->emacsMode = true;
            unset($args[$key]);
        }

        if (false !== ($key = array_search('-verbose', $args, true))) {
            self::$msgOutputLevel = Project::MSG_VERBOSE;
            unset($args[$key]);
        }

        if (false !== ($key = array_search('-debug', $args, true))) {
            self::$msgOutputLevel = Project::MSG_DEBUG;
            unset($args[$key]);
        }

        if (
            false !== ($key = array_search('-silent', $args, true))
            ||
            false !== ($key = array_search('-S', $args, true))
        ) {
            $this->silent = true;
            unset($args[$key]);
        }

        if (false !== ($key = array_search('-propertyfileoverride', $args, true))) {
            $this->propertyFileOverride = true;
            unset($args[$key]);
        }

        // 3) Finally, cycle through to parse remaining args
        //
        $keys = array_keys($args); // Use keys and iterate to max(keys) since there may be some gaps
        $max = $keys ? max($keys) : -1;
        for ($i = 0; $i <= $max; $i++) {

            if (!array_key_exists($i, $args)) {
                // skip this argument, since it must have been removed above.
                continue;
            }

            $arg = $args[$i];

            if ($arg == "-logfile") {
                try {
                    // see: http://phing.info/trac/ticket/65
                    if (!isset($args[$i + 1])) {
                        $msg = "You must specify a log file when using the -logfile argument\n";
                        throw new ConfigurationException($msg);
                    } else {
                        $logFile = new PhingFile($args[++$i]);
                        $out = new FileOutputStream($logFile); // overwrite
                        self::setOutputStream($out);
                        self::setErrorStream($out);
                        self::$isLogFileUsed = true;
                    }
                } catch (IOException $ioe) {
                    $msg = "Cannot write on the specified log file. Make sure the path exists and you have write permissions.";
                    throw new ConfigurationException($msg, $ioe);
                }
            } elseif ($arg == "-buildfile" || $arg == "-file" || $arg == "-f") {
                if (!isset($args[$i + 1])) {
                    $msg = "You must specify a buildfile when using the -buildfile argument.";
                    throw new ConfigurationException($msg);
                } else {
                    $this->buildFile = new PhingFile($args[++$i]);
                }
            } elseif ($arg == "-listener") {
                if (!isset($args[$i + 1])) {
                    $msg = "You must specify a listener class when using the -listener argument";
                    throw new ConfigurationException($msg);
                } else {
                    $this->listeners[] = $args[++$i];
                }
            } elseif (StringHelper::startsWith("-D", $arg)) {
                // Evaluating the property information //
                // Checking whether arg. is not just a switch, and next arg. does not starts with switch identifier
                if (('-D' == $arg) && (!StringHelper::startsWith('-', $args[$i + 1]))) {
                    $name = $args[++$i];
                } else {
                    $name = substr($arg, 2);
                }

                $value = null;
                $posEq = strpos($name, "=");
                if ($posEq !== false) {
                    $value = substr($name, $posEq + 1);
                    $name = substr($name, 0, $posEq);
                } elseif ($i < count($args) - 1 && !StringHelper::startsWith("-D", $args[$i + 1])) {
                    $value = $args[++$i];
                }
                self::$definedProps->setProperty($name, $value);
            } elseif ($arg == "-logger") {
                if (!isset($args[$i + 1])) {
                    $msg = "You must specify a classname when using the -logger argument";
                    throw new ConfigurationException($msg);
                } else {
                    $this->loggerClassname = $args[++$i];
                }
            } elseif ($arg == "-inputhandler") {
                if ($this->inputHandlerClassname !== null) {
                    throw new ConfigurationException("Only one input handler class may be specified.");
                }
                if (!isset($args[$i + 1])) {
                    $msg = "You must specify a classname when using the -inputhandler argument";
                    throw new ConfigurationException($msg);
                } else {
                    $this->inputHandlerClassname = $args[++$i];
                }
            } elseif ($arg == "-propertyfile") {
                if (!isset($args[$i + 1])) {
                    $msg = "You must specify a filename when using the -propertyfile argument";
                    throw new ConfigurationException($msg);
                } else {
                    $filename = $args[++$i];
                    $fileParserFactory = new FileParserFactory();
                    $fileParser = $fileParserFactory->createParser(pathinfo($filename, PATHINFO_EXTENSION));
                    $p = new Properties(null, $fileParser);
                    $p->load(new PhingFile($filename));
                    foreach ($p->getProperties() as $prop => $value) {
                        if ($this->propertyFileOverride) {
                            self::$definedProps->setProperty($prop, $value);
                        }
                        else {
                            $this->setProperty($prop, $value);
                        }
                    }
                }
            } elseif ($arg == "-keep-going" || $arg == "-k") {
                $this->keepGoingMode = true;
            } elseif ($arg == "-longtargets") {
                self::$definedProps->setProperty('phing.showlongtargets', 1);
            } elseif ($arg == "-projecthelp" || $arg == "-targets" || $arg == "-list" || $arg == "-l" || $arg == "-p") {
                // set the flag to display the targets and quit
                $this->projectHelp = true;
            } elseif ($arg == "-find") {
                // eat up next arg if present, default to build.xml
                if ($i < count($args) - 1) {
                    $this->searchForThis = $args[++$i];
                } else {
                    $this->searchForThis = self::DEFAULT_BUILD_FILENAME;
                }
            } elseif (substr($arg, 0, 1) == "-") {
                // we don't have any more args
                self::printUsage();
                self::$err->write(PHP_EOL);
                throw new ConfigurationException("Unknown argument: " . $arg);
            } else {
                // if it's no other arg, it may be the target
                array_push($this->targets, $arg);
            }
        }

        // if buildFile was not specified on the command line,
        if ($this->buildFile === null) {
            // but -find then search for it
            if ($this->searchForThis !== null) {
                $this->buildFile = $this->_findBuildFile(self::getProperty("user.dir"), $this->searchForThis);
            } else {
                $this->buildFile = new PhingFile(self::DEFAULT_BUILD_FILENAME);
            }
        }

        try {
            // make sure buildfile (or buildfile.dist) exists
            if (!$this->buildFile->exists()) {
                $distFile = new PhingFile($this->buildFile->getAbsolutePath() . ".dist");
                if (!$distFile->exists()) {
                    throw new ConfigurationException("Buildfile: " . $this->buildFile->__toString(
                        ) . " does not exist!");
                }
                $this->buildFile = $distFile;
            }

            // make sure it's not a directory
            if ($this->buildFile->isDirectory()) {
                throw new ConfigurationException("Buildfile: " . $this->buildFile->__toString() . " is a dir!");
            }
        } catch (IOException $e) {
            // something else happened, buildfile probably not readable
            throw new ConfigurationException("Buildfile: " . $this->buildFile->__toString() . " is not readable!");
        }

        $this->readyToRun = true;
    }

    /**
     * Helper to get the parent file for a given file.
     *
     * @param  PhingFile $file
     *
     * @return PhingFile Parent file or null if none
     */
    private function _getParentFile(PhingFile $file)
    {
        $filename = $file->getAbsolutePath();
        $file = new PhingFile($filename);
        $filename = $file->getParent();

        return ($filename === null) ? null : new PhingFile($filename);
    }

    /**
     * Search parent directories for the build file.
     *
     * Takes the given target as a suffix to append to each
     * parent directory in search of a build file.  Once the
     * root of the file-system has been reached an exception
     * is thrown.
     *
     * @param  string $start Start file path.
     * @param  string $suffix Suffix filename to look for in parents.
     *
     * @throws ConfigurationException
     *
     * @return PhingFile A handle to the build file
     */
    private function _findBuildFile($start, $suffix)
    {
        $startf = new PhingFile($start);
        $parent = new PhingFile($startf->getAbsolutePath());
        $file = new PhingFile($parent, $suffix);

        // check if the target file exists in the current directory
        while (!$file->exists()) {
            // change to parent directory
            $parent = $this->_getParentFile($parent);

            // if parent is null, then we are at the root of the fs,
            // complain that we can't find the build file.
            if ($parent === null) {
                throw new ConfigurationException("Could not locate a build file!");
            }
            // refresh our file handle
            $file = new PhingFile($parent, $suffix);
        }

        return $file;
    }

    /**
     * Executes the build.
     *
     * @throws ConfigurationException
     * @throws Exception
     *
     * @return void
     */
    public function runBuild()
    {

        if (!$this->readyToRun) {
            return;
        }

        $project = new Project();

        self::setCurrentProject($project);
        set_error_handler(array('Phing', 'handlePhpError'));

        $error = null;

        $this->addBuildListeners($project);
        $this->addInputHandler($project);

        // set this right away, so that it can be used in logging.
        $project->setUserProperty("phing.file", $this->buildFile->getAbsolutePath());
        $project->setUserProperty("phing.dir", dirname($this->buildFile->getAbsolutePath()));

        try {
            $project->fireBuildStarted();
            $project->init();
        } catch (Exception $exc) {
            $project->fireBuildFinished($exc);
            throw $exc;
        }

        $project->setKeepGoingMode($this->keepGoingMode);

        $project->setUserProperty("phing.version", $this->getPhingVersion());

        $e = self::$definedProps->keys();
        while (count($e)) {
            $arg = (string) array_shift($e);
            $value = (string) self::$definedProps->getProperty($arg);
            $project->setUserProperty($arg, $value);
        }
        unset($e);

        $project->setUserProperty("phing.file", $this->buildFile->getAbsolutePath());

        // first use the Configurator to create the project object
        // from the given build file.

        try {
            ProjectConfigurator::configureProject($project, $this->buildFile);
        } catch (Exception $exc) {
            $project->fireBuildFinished($exc);
            restore_error_handler();
            self::unsetCurrentProject();
            throw $exc;
        }

        // make sure that we have a target to execute
        if (count($this->targets) === 0) {
            $this->targets[] = $project->getDefaultTarget();
        }

        // make sure that minimum required phing version is satisfied
        try {
            $this->comparePhingVersion($project->getPhingVersion());
        } catch (Exception $exc) {
            $project->fireBuildFinished($exc);
            restore_error_handler();
            self::unsetCurrentProject();
            throw $exc;
        }

        // execute targets if help param was not given
        if (!$this->projectHelp) {

            try {
                $project->executeTargets($this->targets);
            } catch (Exception $exc) {
                $project->fireBuildFinished($exc);
                restore_error_handler();
                self::unsetCurrentProject();
                throw $exc;
            }
        }
        // if help is requested print it
        if ($this->projectHelp) {
            try {
                $this->printDescription($project);
                $this->printTargets($project);
            } catch (Exception $exc) {
                $project->fireBuildFinished($exc);
                restore_error_handler();
                self::unsetCurrentProject();
                throw $exc;
            }
        }

        // finally {
        if (!$this->projectHelp) {
            $project->fireBuildFinished(null);
        }

        restore_error_handler();
        self::unsetCurrentProject();
    }

    /**
     * @param string $version
     *
     * @return int|void
     *
     * @throws BuildException
     * @throws ConfigurationException
     */
    private function comparePhingVersion($version)
    {
        $current = strtolower(self::getPhingVersion());
        $current = trim(str_replace('phing', '', $current));

        // make sure that version checks are not applied to trunk
        if ('dev' === $current) {
            return 1;
        }

        if (-1 == version_compare($current, $version)) {
            throw new BuildException(
                sprintf('Incompatible Phing version (%s). Version "%s" required.', $current, $version));
        }
    }

    /**
     * Bind any registered build listeners to this project.
     *
     * This means adding the logger and any build listeners that were specified
     * with -listener arg.
     *
     * @param  Project $project
     * @throws BuildException
     * @throws ConfigurationException
     * @return void
     */
    private function addBuildListeners(Project $project)
    {
        // Add the default listener
        $project->addBuildListener($this->createLogger());

        foreach ($this->listeners as $listenerClassname) {
            try {
                $clz = Phing::import($listenerClassname);
            } catch (Exception $e) {
                $msg = "Unable to instantiate specified listener "
                    . "class " . $listenerClassname . " : "
                    . $e->getMessage();
                throw new ConfigurationException($msg);
            }

            $listener = new $clz();

            if ($listener instanceof StreamRequiredBuildLogger) {
                throw new ConfigurationException("Unable to add " . $listenerClassname . " as a listener, since it requires explicit error/output streams. (You can specify it as a -logger.)");
            }
            $project->addBuildListener($listener);
        }
    }

    /**
     * Creates the InputHandler and adds it to the project.
     *
     * @param Project $project the project instance.
     *
     * @throws ConfigurationException
     */
    private function addInputHandler(Project $project)
    {
        if ($this->inputHandlerClassname === null) {
            $handler = new DefaultInputHandler();
        } else {
            try {
                $clz = Phing::import($this->inputHandlerClassname);
                $handler = new $clz();
                if ($project !== null && method_exists($handler, 'setProject')) {
                    $handler->setProject($project);
                }
            } catch (Exception $e) {
                $msg = "Unable to instantiate specified input handler "
                    . "class " . $this->inputHandlerClassname . " : "
                    . $e->getMessage();
                throw new ConfigurationException($msg);
            }
        }
        $project->setInputHandler($handler);
    }

    /**
     * Creates the default build logger for sending build events to the log.
     * @throws BuildException
     * @return BuildLogger The created Logger
     */
    private function createLogger()
    {
        if ($this->silent) {
            require_once 'phing/listener/SilentLogger.php';
            $logger = new SilentLogger();
            self::$msgOutputLevel = Project::MSG_WARN;
        } elseif ($this->loggerClassname !== null) {
            self::import($this->loggerClassname);
            // get class name part
            $classname = self::import($this->loggerClassname);
            $logger = new $classname();
            if (!($logger instanceof BuildLogger)) {
                throw new BuildException($classname . ' does not implement the BuildLogger interface.');
            }
        } else {
            require_once 'phing/listener/DefaultLogger.php';
            $logger = new DefaultLogger();
        }
        $logger->setMessageOutputLevel(self::$msgOutputLevel);
        $logger->setOutputStream(self::$out);
        $logger->setErrorStream(self::$err);
        $logger->setEmacsMode($this->emacsMode);

        return $logger;
    }

    /**
     * Sets the current Project
     * @param Project $p
     */
    public static function setCurrentProject($p)
    {
        self::$currentProject = $p;
    }

    /**
     * Unsets the current Project
     */
    public static function unsetCurrentProject()
    {
        self::$currentProject = null;
    }

    /**
     * Gets the current Project.
     * @return Project Current Project or NULL if none is set yet/still.
     */
    public static function getCurrentProject()
    {
        return self::$currentProject;
    }

    /**
     * A static convenience method to send a log to the current (last-setup) Project.
     * If there is no currently-configured Project, then this will do nothing.
     * @param string $message
     * @param int    $priority Project::MSG_INFO, etc.
     */
    public static function log($message, $priority = Project::MSG_INFO)
    {
        $p = self::getCurrentProject();
        if ($p) {
            $p->log($message, $priority);
        }
    }

    /**
     * Error handler for PHP errors encountered during the build.
     * This uses the logging for the currently configured project.
     *
     * @param $level
     * @param string $message
     * @param $file
     * @param $line
     */
    public static function handlePhpError($level, $message, $file, $line)
    {

        // don't want to print suppressed errors
        if (error_reporting() > 0) {

            if (self::$phpErrorCapture) {

                self::$capturedPhpErrors[] = array(
                    'message' => $message,
                    'level' => $level,
                    'line' => $line,
                    'file' => $file
                );

            } else {

                $message = '[PHP Error] ' . $message;
                $message .= ' [line ' . $line . ' of ' . $file . ']';

                switch ($level) {
                    case 16384: // E_USER_DEPRECATED
                    case 8192: // E_DEPRECATED
                    case E_STRICT:
                    case E_NOTICE:
                    case E_USER_NOTICE:
                        self::log($message, Project::MSG_VERBOSE);
                        break;
                    case E_WARNING:
                    case E_USER_WARNING:
                        self::log($message, Project::MSG_WARN);
                        break;
                    case E_ERROR:
                    case E_USER_ERROR:
                    default:
                        self::log($message, Project::MSG_ERR);

                } // switch

            } // if phpErrorCapture

        } // if not @

    }

    /**
     * Begins capturing PHP errors to a buffer.
     * While errors are being captured, they are not logged.
     */
    public static function startPhpErrorCapture()
    {
        self::$phpErrorCapture = true;
        self::$capturedPhpErrors = array();
    }

    /**
     * Stops capturing PHP errors to a buffer.
     * The errors will once again be logged after calling this method.
     */
    public static function stopPhpErrorCapture()
    {
        self::$phpErrorCapture = false;
    }

    /**
     * Clears the captured errors without affecting the starting/stopping of the capture.
     */
    public static function clearCapturedPhpErrors()
    {
        self::$capturedPhpErrors = array();
    }

    /**
     * Gets any PHP errors that were captured to buffer.
     * @return array array('message' => message, 'line' => line number, 'file' => file name, 'level' => error level)
     */
    public static function getCapturedPhpErrors()
    {
        return self::$capturedPhpErrors;
    }

    /**  Prints the usage of how to use this class */
    public static function printUsage()
    {

        $msg = "";
        $msg .= "phing [options] [target [target2 [target3] ...]]" . PHP_EOL;
        $msg .= "Options: " . PHP_EOL;
        $msg .= "  -h -help               print this message" . PHP_EOL;
        $msg .= "  -l -list               list available targets in this project" . PHP_EOL;
        $msg .= "  -v -version            print the version information and exit" . PHP_EOL;
        $msg .= "  -q -quiet              be extra quiet" . PHP_EOL;
        $msg .= "  -S -silent             print nothing but task outputs and build failures" . PHP_EOL;
        $msg .= "  -verbose               be extra verbose" . PHP_EOL;
        $msg .= "  -debug                 print debugging information" . PHP_EOL;
        $msg .= "  -emacs, -e             produce logging information without adornments" . PHP_EOL;
        $msg .= "  -diagnostics           print diagnostics information" . PHP_EOL;
        $msg .= "  -longtargets           show target descriptions during build" . PHP_EOL;
        $msg .= "  -logfile <file>        use given file for log" . PHP_EOL;
        $msg .= "  -logger <classname>    the class which is to perform logging" . PHP_EOL;
        $msg .= "  -listener <classname>  add an instance of class as a project listener" . PHP_EOL;
        $msg .= "  -f -buildfile <file>   use given buildfile" . PHP_EOL;
        $msg .= "  -D<property>=<value>   use value for given property" . PHP_EOL;
        $msg .= "  -keep-going, -k        execute all targets that do not depend" . PHP_EOL;
        $msg .= "                         on failed target(s)" . PHP_EOL;
        $msg .= "  -propertyfile <file>   load all properties from file" . PHP_EOL;
        $msg .= "  -propertyfileoverride  values in property file override existing values" . PHP_EOL;
        $msg .= "  -find <file>           search for buildfile towards the root of the" . PHP_EOL;
        $msg .= "                         filesystem and use it" . PHP_EOL;
        $msg .= "  -inputhandler <file>   the class to use to handle user input" . PHP_EOL;
        //$msg .= "  -recursive <file>      search for buildfile downwards and use it" . PHP_EOL;
        $msg .= PHP_EOL;
        $msg .= "Report bugs to <dev@phing.tigris.org>" . PHP_EOL;
        self::$err->write($msg);
    }

    /**
     * Prints the current Phing version.
     */
    public static function printVersion()
    {
        self::$out->write(self::getPhingVersion() . PHP_EOL);
    }

    /**
     * Copied from https://github.com/sebastianbergmann/version/blob/master/src/Version.php
     * @param string $path
     * @return bool|string
     */
    private static function getGitInformation($path)
    {
        if (!is_dir($path . DIRECTORY_SEPARATOR . '.git')) {
            return false;
        }
        $dir = getcwd();
        chdir($path);
        $result = @exec('git describe --tags 2>&1', $output, $returnCode);
        chdir($dir);
        if ($returnCode !== 0) {
            return false;
        }
        return $result;
    }

    /**
     * Gets the current Phing version based on VERSION.TXT file.
     *
     * @throws ConfigurationException
     *
     * @return string
     */
    public static function getPhingVersion()
    {
        $path = dirname(dirname(dirname(__FILE__)));

        $gitInformation = self::getGitInformation($path);

        if ($gitInformation) {
            return "Phing " . $gitInformation;
        }

        $versionPath = self::getResourcePath("phing/etc/VERSION.TXT");
        if ($versionPath === null) {
            $versionPath = self::getResourcePath("etc/VERSION.TXT");
        }
        if ($versionPath === null) {
            throw new ConfigurationException("No VERSION.TXT file found; try setting phing.home environment variable.");
        }
        try { // try to read file
            $file = new PhingFile($versionPath);
            $reader = new FileReader($file);
            $phingVersion = trim($reader->read());
        } catch (IOException $iox) {
            throw new ConfigurationException("Can't read version information file");
        }

        return "Phing " . $phingVersion;
    }

    /**
     * Print the project description, if any
     *
     * @param Project $project
     *
     * @throws IOException
     */
    public static function printDescription(Project $project)
    {
        if ($project->getDescription() !== null) {
            self::$out->write($project->getDescription() . PHP_EOL);
        }
    }

    /** Print out a list of all targets in the current buildfile
     * @param $project
     */
    public function printTargets($project)
    {
        // find the target with the longest name
        $maxLength = 0;
        $targets = $project->getTargets();
        $targetNames = array_keys($targets);
        $targetName = null;
        $targetDescription = null;
        $currentTarget = null;

        // split the targets in top-level and sub-targets depending
        // on the presence of a description

        $subNames = array();
        $topNameDescMap = array();

        foreach ($targets as $currentTarget) {
            $targetName = $currentTarget->getName();
            $targetDescription = $currentTarget->getDescription();
            if ($currentTarget->isHidden()) {
                continue;
            }

            // subtargets are targets w/o descriptions
            if ($targetDescription === null) {
                $subNames[] = $targetName;
            } else {
                // topNames and topDescriptions are handled later
                // here we store in hash map (for sorting purposes)
                $topNameDescMap[$targetName] = $targetDescription;
                if (strlen($targetName) > $maxLength) {
                    $maxLength = strlen($targetName);
                }
            }
        }

        // Sort the arrays
        sort($subNames); // sort array values, resetting keys (which are numeric)
        ksort($topNameDescMap); // sort the keys (targetName) keeping key=>val associations

        $topNames = array_keys($topNameDescMap);
        $topDescriptions = array_values($topNameDescMap);

        $defaultTarget = $project->getDefaultTarget();

        if ($defaultTarget !== null && $defaultTarget !== "") {
            $defaultName = array();
            $defaultDesc = array();
            $defaultName[] = $defaultTarget;

            $indexOfDefDesc = array_search($defaultTarget, $topNames, true);
            if ($indexOfDefDesc !== false && $indexOfDefDesc >= 0) {
                $defaultDesc = array();
                $defaultDesc[] = $topDescriptions[$indexOfDefDesc];
            }

            $this->_printTargets($defaultName, $defaultDesc, "Default target:", $maxLength);

        }
        $this->_printTargets($topNames, $topDescriptions, "Main targets:", $maxLength);
        $this->_printTargets($subNames, null, "Subtargets:", 0);
    }

    /**
     * Writes a formatted list of target names with an optional description.
     *
     * @param array  $names        The names to be printed.
     *                             Must not be <code>null</code>.
     * @param array  $descriptions The associated target descriptions.
     *                             May be <code>null</code>, in which case
     *                             no descriptions are displayed.
     *                             If non-<code>null</code>, this should have
     *                             as many elements as <code>names</code>.
     * @param string $heading      The heading to display.
     *                             Should not be <code>null</code>.
     * @param int    $maxlen       The maximum length of the names of the targets.
     *                             If descriptions are given, they are padded to this
     *                             position so they line up (so long as the names really
     *                             <i>are</i> shorter than this).
     */
    private function _printTargets($names, $descriptions, $heading, $maxlen)
    {

        $spaces = '  ';
        while (strlen($spaces) < $maxlen) {
            $spaces .= $spaces;
        }
        $msg = "";
        $msg .= $heading . PHP_EOL;
        $msg .= str_repeat("-", 79) . PHP_EOL;

        $total = count($names);
        for ($i = 0; $i < $total; $i++) {
            $msg .= " ";
            $msg .= $names[$i];
            if (!empty($descriptions)) {
                $msg .= substr($spaces, 0, $maxlen - strlen($names[$i]) + 2);
                $msg .= $descriptions[$i];
            }
            $msg .= PHP_EOL;
        }
        if ($total > 0) {
            self::$out->write($msg . PHP_EOL);
        }
    }

    /**
     * Import a path, supporting the following conventions:
     * - PEAR style (@link http://pear.php.net/manual/en/standards.naming.php)
     * - PSR-0 (@link https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md)
     * - dot-path
     *
     * @param string         $dotPath   Path
     * @param mixed          $classpath String or object supporting __toString()
     *
     * @return string         The unqualified classname (which can be instantiated).
     *
     * @throws BuildException - if cannot find the specified file
     */
    public static function import($dotPath, $classpath = null)
    {

        if (strpos($dotPath, '.') !== false) {
            $classname = StringHelper::unqualify($dotPath);
        } else {
            $classname = $dotPath;
            $dotPath = '';
            $shortClassName = $classname;
            if (($lastNsPos = strripos($shortClassName, '\\'))) {
                $namespace = substr($shortClassName, 0, $lastNsPos);
                $shortClassName = substr($shortClassName, $lastNsPos + 1);
                $dotPath = str_replace('\\', '.', $namespace) . '.';
            }
            $dotPath .= str_replace('_', '.', $shortClassName);
        }

        // first check to see that the class specified hasn't already been included.
        // (this also handles case where this method is called w/ a classname rather than dotpath)
        if (class_exists($classname)) {
            return $classname;
        }

        $dotClassname = basename($dotPath);
        $dotClassnamePos = strlen($dotPath) - strlen($dotClassname);

        // 1- temporarily replace escaped '.' with another illegal char (#)
        $tmp = str_replace('\.', '##', $dotClassname);
        // 2- swap out the remaining '.' with DIR_SEP
        $tmp = strtr($tmp, '.', DIRECTORY_SEPARATOR);
        // 3- swap back the escaped '.'
        $tmp = str_replace('##', '.', $tmp);

        $classFile = $tmp . ".php";

        $path = substr_replace($dotPath, $classFile, $dotClassnamePos);

        Phing::__import($path, $classpath);

        return $classname;
    }

    /**
     * Import a PHP file
     *
     * @param  string $path Path to the PHP file
     * @param  mixed $classpath String or object supporting __toString()
     *
     * @throws ConfigurationException
     */
    public static function __import($path, $classpath = null)
    {

        if ($classpath) {

            // Apparently casting to (string) no longer invokes __toString() automatically.
            if (is_object($classpath)) {
                $classpath = $classpath->__toString();
            }

            // classpaths are currently additive, but we also don't want to just
            // indiscriminantly prepand/append stuff to the include_path.  This means
            // we need to parse current incldue_path, and prepend any
            // specified classpath locations that are not already in the include_path.
            //
            // NOTE:  the reason why we do it this way instead of just changing include_path
            // and then changing it back, is that in many cases applications (e.g. Propel) will
            // include/require class files from within method calls.  This means that not all
            // necessary files will be included in this import() call, and hence we can't
            // change the include_path back without breaking those apps.  While this method could
            // be more expensive than switching & switching back (not sure, but maybe), it makes it
            // possible to write far less expensive run-time applications (e.g. using Propel), which is
            // really where speed matters more.

            $curr_parts = Phing::explodeIncludePath();
            $add_parts = Phing::explodeIncludePath($classpath);
            $new_parts = array_diff($add_parts, $curr_parts);
            if ($new_parts) {
                set_include_path(implode(PATH_SEPARATOR, array_merge($new_parts, $curr_parts)));
            }
        }

        $ret = include_once $path;

        if ($ret === false) {
            $msg = "Error importing $path";
            if (self::getMsgOutputLevel() >= Project::MSG_DEBUG) {
                $x = new Exception("for-path-trace-only");
                $msg .= $x->getTraceAsString();
            }
            throw new ConfigurationException($msg);
        }
    }

    /**
     * Looks on include path for specified file.
     *
     * @param string $path
     *
     * @return string File found (null if no file found).
     */
    public static function getResourcePath($path)
    {

        if (self::$importPaths === null) {
            self::$importPaths = self::explodeIncludePath();
        }

        $path = str_replace('\\', DIRECTORY_SEPARATOR, $path);
        $path = str_replace('/', DIRECTORY_SEPARATOR, $path);

        foreach (self::$importPaths as $prefix) {
            $testPath = $prefix . DIRECTORY_SEPARATOR . $path;
            if (file_exists($testPath)) {
                return $testPath;
            }
        }

        // Check for the property phing.home
        $homeDir = self::getProperty(self::PHING_HOME);
        if ($homeDir) {
            $testPath = $homeDir . DIRECTORY_SEPARATOR . $path;
            if (file_exists($testPath)) {
                return $testPath;
            }
        }

        // Check for the phing home of phar archive
        if (strpos(self::$importPaths[0], 'phar://') === 0) {
            $testPath = self::$importPaths[0] . '/../' . $path;
            if (file_exists($testPath)) {
                return $testPath;
            }
        }

        // If we are using this via PEAR then check for the file in the data dir
        // This is a bit of a hack, but works better than previous solution of assuming
        // data_dir is on the include_path.
        $dataDir = '@DATA-DIR@';
        if ($dataDir{0} != '@') { // if we're using PEAR then the @ DATA-DIR @ token will have been substituted.
            if (!file_exists($dataDir)) {
                self::log("The PEAR data_dir setting is incorrect: {$dataDir}.", Project::MSG_ERR);
                self::log("Please edit using 'pear config-set data_dir ...' and re-install Phing.", Project::MSG_ERR);

                return null;
            }

            $testPath = $dataDir . DIRECTORY_SEPARATOR . $path;
            if (file_exists($testPath)) {
                return $testPath;
            }
        } else {
            // We're not using PEAR, so do one additional check based on path of
            // current file (Phing.php)
            $maybeHomeDir = realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..');
            $testPath = $maybeHomeDir . DIRECTORY_SEPARATOR . $path;
            if (file_exists($testPath)) {
                return $testPath;
            }
        }

        return null;
    }

    /**
     * Explode an include path into an array
     *
     * If no path provided, uses current include_path. Works around issues that
     * occur when the path includes stream schemas.
     *
     * Pulled from Zend_Loader::explodeIncludePath() in ZF1.
     *
     * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
     * @license http://framework.zend.com/license/new-bsd New BSD License
     * @param  string|null $path
     * @return array
     */
    public static function explodeIncludePath($path = null)
    {
        if (null === $path) {
            $path = get_include_path();
        }

        if (PATH_SEPARATOR == ':') {
            // On *nix systems, include_paths which include paths with a stream
            // schema cannot be safely explode'd, so we have to be a bit more
            // intelligent in the approach.
            $paths = preg_split('#:(?!//)#', $path);
        } else {
            $paths = explode(PATH_SEPARATOR, $path);
        }

        return $paths;
    }

    // -------------------------------------------------------------------------------------------
    // System-wide methods (moved from System class, which had namespace conflicts w/ PEAR System)
    // -------------------------------------------------------------------------------------------

    /**
     * Set System constants which can be retrieved by calling Phing::getProperty($propName).
     * @return void
     */
    private static function setSystemConstants()
    {

        /*
         * PHP_OS returns on
         *   WindowsNT4.0sp6  => WINNT
         *   Windows2000      => WINNT
         *   Windows ME       => WIN32
         *   Windows 98SE     => WIN32
         *   FreeBSD 4.5p7    => FreeBSD
         *   Redhat Linux     => Linux
         *   Mac OS X         => Darwin
         */
        self::setProperty('host.os', PHP_OS);

        // this is used by some tasks too
        self::setProperty('os.name', PHP_OS);

        // it's still possible this won't be defined,
        // e.g. if Phing is being included in another app w/o
        // using the phing.php script.
        if (!defined('PHP_CLASSPATH')) {
            define('PHP_CLASSPATH', get_include_path());
        }

        self::setProperty('php.classpath', PHP_CLASSPATH);

        // try to determine the host filesystem and set system property
        // used by Fileself::getFileSystem to instantiate the correct
        // abstraction layer

        switch (strtoupper(PHP_OS)) {
            case 'WINNT':
                self::setProperty('host.fstype', 'WINNT');
                break;
            case 'WIN32':
                self::setProperty('host.fstype', 'WIN32');
                break;
            default:
                self::setProperty('host.fstype', 'UNIX');
                break;
        }

        if (defined('PHP_BINARY')) {
            self::setProperty('php.interpreter', PHP_BINARY);
        } else {
            self::setProperty('php.interpreter', getenv('PHP_COMMAND'));
        }
        self::setProperty('line.separator', PHP_EOL);
        self::setProperty('php.version', PHP_VERSION);
        self::setProperty('php.tmpdir', sys_get_temp_dir());
        if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
            self::setProperty('user.home', getenv('HOME'));
        } else {
            self::setProperty('user.home', getenv('HOMEDRIVE') . getenv('HOMEPATH'));
        }
        self::setProperty('application.startdir', getcwd());
        self::setProperty('phing.startTime', gmdate('D, d M Y H:i:s', time()) . ' GMT');
        self::setProperty('php.tmpdir', sys_get_temp_dir());

        // try to detect machine dependent information
        $sysInfo = array();
        if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' && function_exists("posix_uname")) {
            $sysInfo = posix_uname();
        } else {
            $sysInfo['nodename'] = php_uname('n');
            $sysInfo['machine'] = php_uname('m');
            //this is a not so ideal substition, but maybe better than nothing
            $sysInfo['domain'] = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : "unknown";
            $sysInfo['release'] = php_uname('r');
            $sysInfo['version'] = php_uname('v');
        }

        self::setProperty("host.name", isset($sysInfo['nodename']) ? $sysInfo['nodename'] : "unknown");
        self::setProperty("host.arch", isset($sysInfo['machine']) ? $sysInfo['machine'] : "unknown");
        self::setProperty("host.domain", isset($sysInfo['domain']) ? $sysInfo['domain'] : "unknown");
        self::setProperty("host.os.release", isset($sysInfo['release']) ? $sysInfo['release'] : "unknown");
        self::setProperty("host.os.version", isset($sysInfo['version']) ? $sysInfo['version'] : "unknown");
        unset($sysInfo);
    }

    /**
     * This gets a property that was set via command line or otherwise passed into Phing.
     * "Defined" in this case means "externally defined".  The reason this method exists is to
     * provide a public means of accessing commandline properties for (e.g.) logger or listener
     * scripts.  E.g. to specify which logfile to use, PearLogger needs to be able to access
     * the pear.log.name property.
     *
     * @param  string $name
     * @return string value of found property (or null, if none found).
     */
    public static function getDefinedProperty($name)
    {
        return self::$definedProps->getProperty($name);
    }

    /**
     * This sets a property that was set via command line or otherwise passed into Phing.
     *
     * @param string $name
     * @param mixed $value
     * @return mixed value of found property (or null, if none found).
     */
    public static function setDefinedProperty($name, $value)
    {
        return self::$definedProps->setProperty($name, $value);
    }

    /**
     * Returns property value for a System property.
     * System properties are "global" properties like application.startdir,
     * and user.dir.  Many of these correspond to similar properties in Java
     * or Ant.
     *
     * @param  string $propName
     * @return string Value of found property (or null, if none found).
     */
    public static function getProperty($propName)
    {

        // some properties are detemined on each access
        // some are cached, see below

        // default is the cached value:
        $val = isset(self::$properties[$propName]) ? self::$properties[$propName] : null;

        // special exceptions
        switch ($propName) {
            case 'user.dir':
                $val = getcwd();
                break;
        }

        return $val;
    }

    /** Retuns reference to all properties*/
    public static function &getProperties()
    {
        return self::$properties;
    }

    /**
     * @param $propName
     * @param $propValue
     * @return string
     */
    public static function setProperty($propName, $propValue)
    {
        $propName = (string) $propName;
        $oldValue = self::getProperty($propName);
        self::$properties[$propName] = $propValue;

        return $oldValue;
    }

    /**
     * @return float
     */
    public static function currentTimeMillis()
    {
        list($usec, $sec) = explode(" ", microtime());

        return ((float) $usec + (float) $sec);
    }

    /**
     * Sets the include path to PHP_CLASSPATH constant (if this has been defined).
     * @return void
     * @throws ConfigurationException - if the include_path could not be set (for some bizarre reason)
     */
    private static function setIncludePaths()
    {
        if (defined('PHP_CLASSPATH')) {
            $result = set_include_path(PHP_CLASSPATH);
            if ($result === false) {
                throw new ConfigurationException("Could not set PHP include_path.");
            }
            self::$origIniSettings['include_path'] = $result; // save original value for setting back later
        }
    }

    /**
     * Converts shorthand notation values as returned by ini_get()
     * @see http://www.php.net/ini_get
     * @param string $val
     * @return int
     */
    private static function convertShorthand($val)
    {
        $val = trim($val);
        $last = strtolower($val[strlen($val) - 1]);

        $val = (int) $val;

        switch ($last) {
            // The 'G' modifier is available since PHP 5.1.0
            case 'g':
                $val *= 1024;
            case 'm':
                $val *= 1024;
            case 'k':
                $val *= 1024;
        }

        return $val;
    }

    /**
     * Sets PHP INI values that Phing needs.
     * @return void
     */
    private static function setIni()
    {

        self::$origIniSettings['error_reporting'] = error_reporting(E_ALL);

        // We won't bother storing original max_execution_time, since 1) the value in
        // php.ini may be wrong (and there's no way to get the current value) and
        // 2) it would mean something very strange to set it to a value less than time script
        // has already been running, which would be the likely change.

        set_time_limit(0);

        self::$origIniSettings['magic_quotes_gpc'] = ini_set('magic_quotes_gpc', 'off');
        self::$origIniSettings['short_open_tag'] = ini_set('short_open_tag', 'off');
        self::$origIniSettings['default_charset'] = ini_set('default_charset', 'iso-8859-1');
        self::$origIniSettings['register_globals'] = ini_set('register_globals', 'off');
        self::$origIniSettings['allow_call_time_pass_reference'] = ini_set('allow_call_time_pass_reference', 'on');
        self::$origIniSettings['track_errors'] = ini_set('track_errors', 1);

        $mem_limit = (int) self::convertShorthand(ini_get('memory_limit'));
        if ($mem_limit < (32 * 1024 * 1024) && $mem_limit > -1) {
            // We do *not* need to save the original value here, since we don't plan to restore
            // this after shutdown (we don't trust the effectiveness of PHP's garbage collection).
            ini_set('memory_limit', '32M'); // nore: this may need to be higher for many projects
        }
    }

    /**
     * Restores [most] PHP INI values to their pre-Phing state.
     *
     * Currently the following settings are not restored:
     *  - max_execution_time (because getting current time limit is not possible)
     *  - memory_limit (which may have been increased by Phing)
     *
     * @return void
     */
    private static function restoreIni()
    {
        foreach (self::$origIniSettings as $settingName => $settingValue) {
            switch ($settingName) {
                case 'error_reporting':
                    error_reporting($settingValue);
                    break;
                default:
                    ini_set($settingName, $settingValue);
            }
        }
    }

    /**
     * Returns reference to Timer object.
     * @return Timer
     */
    public static function getTimer()
    {
        if (self::$timer === null) {
            include_once 'phing/system/util/Timer.php';
            self::$timer = new Timer();
        }

        return self::$timer;
    }

    /**
     * Start up Phing.
     * Sets up the Phing environment but does not initiate the build process.
     * @return void
     * @throws Exception - If the Phing environment cannot be initialized.
     */
    public static function startup()
    {

        // setup STDOUT and STDERR defaults
        self::initializeOutputStreams();

        // some init stuff
        self::getTimer()->start();

        self::setSystemConstants();
        self::setIncludePaths();
        self::setIni();
    }

    /**
     * Halts the system.
     * @deprecated This method is deprecated and is no longer called by Phing internally.  Any
     *              normal shutdown routines are handled by the shutdown() method.
     * @see shutdown()
     */
    public static function halt()
    {
        self::shutdown();
    }

    /**
     * Performs any shutdown routines, such as stopping timers.
     * @return void
     */
    public static function shutdown()
    {
        self::$msgOutputLevel = Project::MSG_INFO;
        self::restoreIni();
        self::getTimer()->stop();
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/system/io/PhingFile.php';
include_once 'phing/util/FileUtils.php';
include_once 'phing/TaskAdapter.php';
include_once 'phing/util/StringHelper.php';
include_once 'phing/BuildEvent.php';
include_once 'phing/input/DefaultInputHandler.php';
include_once 'phing/types/PropertyValue.php';

/**
 *  The Phing project class. Represents a completely configured Phing project.
 *  The class defines the project and all tasks/targets. It also contains
 *  methods to start a build as well as some properties and FileSystem
 *  abstraction.
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @author    Hans Lellelid <hans@xmpl.org>
 *
 * @package   phing
 */
class Project
{

    // Logging level constants.
    const MSG_DEBUG = 4;
    const MSG_VERBOSE = 3;
    const MSG_INFO = 2;
    const MSG_WARN = 1;
    const MSG_ERR = 0;

    /** contains the targets */
    private $targets = array();
    /** global filterset (future use) */
    private $globalFilterSet = array();
    /**  all globals filters (future use) */
    private $globalFilters = array();

    /** Project properties map (usually String to String). */
    private $properties = array();

    /**
     * Map of "user" properties (as created in the Ant task, for example).
     * Note that these key/value pairs are also always put into the
     * project properties, so only the project properties need to be queried.
     * Mapping is String to String.
     */
    private $userProperties = array();

    /**
     * Map of inherited "user" properties - that are those "user"
     * properties that have been created by tasks and not been set
     * from the command line or a GUI tool.
     * Mapping is String to String.
     */
    private $inheritedProperties = array();

    /** task definitions for this project*/
    private $taskdefs = array();

    /** type definitions for this project */
    private $typedefs = array();

    /** holds ref names and a reference to the referred object*/
    private $references = array();

    /** The InputHandler being used by this project. */
    private $inputHandler;

    /* -- properties that come in via xml attributes -- */

    /** basedir (PhingFile object) */
    private $basedir;

    /** the default target name */
    private $defaultTarget = 'all';

    /** project name (required) */
    private $name;

    /** project description */
    private $description;

    /** require phing version */
    private $phingVersion;

    /** a FileUtils object */
    private $fileUtils;

    /**  Build listeneers */
    private $listeners = array();

    /**
     * Keep going flag.
     */
    private $keepGoingMode = false;

    /**
     *  Constructor, sets any default vars.
     */
    public function __construct()
    {
        $this->fileUtils = new FileUtils();
        $this->inputHandler = new DefaultInputHandler();
    }

    /**
     * Sets the input handler
     * @param InputHandler $handler
     */
    public function setInputHandler(InputHandler $handler)
    {
        $this->inputHandler = $handler;
    }

    /**
     * Retrieves the current input handler.
     * @return InputHandler
     */
    public function getInputHandler()
    {
        return $this->inputHandler;
    }

    /** inits the project, called from main app */
    public function init()
    {
        // set builtin properties
        $this->setSystemProperties();

        // load default tasks
        $taskdefs = Phing::getResourcePath("phing/tasks/defaults.properties");

        try { // try to load taskdefs
            $props = new Properties();
            $in = new PhingFile((string) $taskdefs);

            if ($in === null) {
                throw new BuildException("Can't load default task list");
            }
            $props->load($in);

            $enum = $props->propertyNames();
            foreach ($enum as $key) {
                $value = $props->getProperty($key);
                $this->addTaskDefinition($key, $value);
            }
        } catch (IOException $ioe) {
            throw new BuildException("Can't load default task list");
        }

        // load default tasks
        $typedefs = Phing::getResourcePath("phing/types/defaults.properties");

        try { // try to load typedefs
            $props = new Properties();
            $in = new PhingFile((string) $typedefs);
            if ($in === null) {
                throw new BuildException("Can't load default datatype list");
            }
            $props->load($in);

            $enum = $props->propertyNames();
            foreach ($enum as $key) {
                $value = $props->getProperty($key);
                $this->addDataTypeDefinition($key, $value);
            }
        } catch (IOException $ioe) {
            throw new BuildException("Can't load default datatype list");
        }
    }

    /** returns the global filterset (future use) */
    public function getGlobalFilterSet()
    {
        return $this->globalFilterSet;
    }

    // ---------------------------------------------------------
    // Property methods
    // ---------------------------------------------------------

    /**
     * Sets a property. Any existing property of the same name
     * is overwritten, unless it is a user property.
     * @param  string $name  The name of property to set.
     *                       Must not be <code>null</code>.
     * @param  string $value The new value of the property.
     *                       Must not be <code>null</code>.
     * @return void
     */
    public function setProperty($name, $value)
    {

        // command line properties take precedence
        if (isset($this->userProperties[$name])) {
            $this->log("Override ignored for user property " . $name, Project::MSG_VERBOSE);

            return;
        }

        if (isset($this->properties[$name])) {
            $this->log("Overriding previous definition of property " . $name, Project::MSG_VERBOSE);
        }

        $this->log("Setting project property: " . $name . " -> " . $value, Project::MSG_DEBUG);
        $this->properties[$name] = $value;
        $this->addReference($name, new PropertyValue($value));
    }

    /**
     * Sets a property if no value currently exists. If the property
     * exists already, a message is logged and the method returns with
     * no other effect.
     *
     * @param string $name  The name of property to set.
     *                      Must not be <code>null</code>.
     * @param string $value The new value of the property.
     *                      Must not be <code>null</code>.
     * @since 2.0
     */
    public function setNewProperty($name, $value)
    {
        if (isset($this->properties[$name])) {
            $this->log("Override ignored for property " . $name, Project::MSG_DEBUG);

            return;
        }
        $this->log("Setting project property: " . $name . " -> " . $value, Project::MSG_DEBUG);
        $this->properties[$name] = $value;
        $this->addReference($name, new PropertyValue($value));
    }

    /**
     * Sets a user property, which cannot be overwritten by
     * set/unset property calls. Any previous value is overwritten.
     * @param string $name  The name of property to set.
     *                      Must not be <code>null</code>.
     * @param string $value The new value of the property.
     *                      Must not be <code>null</code>.
     * @see #setProperty()
     */
    public function setUserProperty($name, $value)
    {
        $this->log("Setting user project property: " . $name . " -> " . $value, Project::MSG_DEBUG);
        $this->userProperties[$name] = $value;
        $this->properties[$name] = $value;
        $this->addReference($name, new PropertyValue($value));
    }

    /**
     * Sets a user property, which cannot be overwritten by set/unset
     * property calls. Any previous value is overwritten. Also marks
     * these properties as properties that have not come from the
     * command line.
     *
     * @param string $name  The name of property to set.
     *                      Must not be <code>null</code>.
     * @param string $value The new value of the property.
     *                      Must not be <code>null</code>.
     * @see #setProperty()
     */
    public function setInheritedProperty($name, $value)
    {
        $this->inheritedProperties[$name] = $value;
        $this->setUserProperty($name, $value);
    }

    /**
     * Sets a property unless it is already defined as a user property
     * (in which case the method returns silently).
     *
     * @param string $name The name of the property.
     *             Must not be <code>null</code>.
     * @param string $value The property value. Must not be <code>null</code>.
     */
    private function setPropertyInternal($name, $value)
    {
        if (isset($this->userProperties[$name])) {
            $this->log("Override ignored for user property " . $name, Project::MSG_VERBOSE);

            return;
        }
        $this->properties[$name] = $value;
        $this->addReference($name, new PropertyValue($value));
    }

    /**
     * Returns the value of a property, if it is set.
     *
     * @param  string $name The name of the property.
     *                      May be <code>null</code>, in which case
     *                      the return value is also <code>null</code>.
     * @return string The property value, or <code>null</code> for no match
     *                     or if a <code>null</code> name is provided.
     */
    public function getProperty($name)
    {
        if (!isset($this->properties[$name])) {
            return null;
        }
        $found = $this->properties[$name];
        // check to see if there are unresolved property references
        if (false !== strpos($found, '${')) {
            // attempt to resolve properties
            $found = $this->replaceProperties($found);
            // save resolved value
            $this->properties[$name] = $found;
        }

        return $found;
    }

    /**
     * Replaces ${} style constructions in the given value with the
     * string value of the corresponding data types.
     *
     * @param string $value The value string to be scanned for property references.
     *                      May be <code>null</code>.
     *
     * @return string the given string with embedded property names replaced
     *                by values, or <code>null</code> if the given string is
     *                <code>null</code>.
     *
     * @exception BuildException if the given value has an unclosed
     *                           property name, e.g. <code>${xxx</code>
     */
    public function replaceProperties($value)
    {
        return ProjectConfigurator::replaceProperties($this, $value, $this->properties);
    }

    /**
     * Returns the value of a user property, if it is set.
     *
     * @param  string $name The name of the property.
     *                      May be <code>null</code>, in which case
     *                      the return value is also <code>null</code>.
     * @return string The property value, or <code>null</code> for no match
     *                     or if a <code>null</code> name is provided.
     */
    public function getUserProperty($name)
    {
        if (!isset($this->userProperties[$name])) {
            return null;
        }

        return $this->userProperties[$name];
    }

    /**
     * Returns a copy of the properties table.
     * @return array A hashtable containing all properties
     *               (including user properties).
     */
    public function getProperties()
    {
        return $this->properties;
    }

    /**
     * Returns a copy of the user property hashtable
     * @return array a hashtable containing just the user properties
     */
    public function getUserProperties()
    {
        return $this->userProperties;
    }

    /**
     * Copies all user properties that have been set on the command
     * line or a GUI tool from this instance to the Project instance
     * given as the argument.
     *
     * <p>To copy all "user" properties, you will also have to call
     * {@link #copyInheritedProperties copyInheritedProperties}.</p>
     *
     * @param  Project $other the project to copy the properties to.  Must not be null.
     * @return void
     * @since phing 2.0
     */
    public function copyUserProperties(Project $other)
    {
        foreach ($this->userProperties as $arg => $value) {
            if (isset($this->inheritedProperties[$arg])) {
                continue;
            }
            $other->setUserProperty($arg, $value);
        }
    }

    /**
     * Copies all user properties that have not been set on the
     * command line or a GUI tool from this instance to the Project
     * instance given as the argument.
     *
     * <p>To copy all "user" properties, you will also have to call
     * {@link #copyUserProperties copyUserProperties}.</p>
     *
     * @param Project $other the project to copy the properties to.  Must not be null.
     *
     * @since phing 2.0
     */
    public function copyInheritedProperties(Project $other)
    {
        foreach ($this->userProperties as $arg => $value) {
            if ($other->getUserProperty($arg) !== null) {
                continue;
            }
            $other->setInheritedProperty($arg, $value);
        }
    }

    // ---------------------------------------------------------
    //  END Properties methods
    // ---------------------------------------------------------

    /**
     * Sets default target
     * @param string $targetName
     */
    public function setDefaultTarget($targetName)
    {
        $this->defaultTarget = (string) trim($targetName);
    }

    /**
     * Returns default target
     * @return string
     */
    public function getDefaultTarget()
    {
        return (string) $this->defaultTarget;
    }

    /**
     * Sets the name of the current project
     *
     * @param string $name name of project
     * @return void
     * @author Andreas Aderhold, andi@binarycloud.com
     */
    public function setName($name)
    {
        $this->name = (string) trim($name);
        $this->setProperty("phing.project.name", $this->name);
    }

    /**
     * Returns the name of this project
     *
     * @return string projectname
     * @author Andreas Aderhold, andi@binarycloud.com
     */
    public function getName()
    {
        return (string) $this->name;
    }

    /**
     * Set the projects description
     * @param string $description
     */
    public function setDescription($description)
    {
        $this->description = (string) trim($description);
    }

    /**
     * return the description, null otherwise
     * @return string|null
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Set the minimum required phing version
     * @param string $version
     */
    public function setPhingVersion($version)
    {
        $version = str_replace('phing', '', strtolower($version));
        $this->phingVersion = (string) trim($version);
    }

    /**
     * Get the minimum required phing version
     * @return string
     */
    public function getPhingVersion()
    {
        if ($this->phingVersion === null) {
            $this->setPhingVersion(Phing::getPhingVersion());
        }

        return $this->phingVersion;
    }

    /**
     * Set basedir object from xm
     * @param PhingFile|string $dir
     * @throws BuildException
     */
    public function setBasedir($dir)
    {
        if ($dir instanceof PhingFile) {
            $dir = $dir->getAbsolutePath();
        }

        $dir = $this->fileUtils->normalize($dir);
        $dir = FileSystem::getFilesystem()->canonicalize($dir);

        $dir = new PhingFile((string) $dir);
        if (!$dir->exists()) {
            throw new BuildException("Basedir " . $dir->getAbsolutePath() . " does not exist");
        }
        if (!$dir->isDirectory()) {
            throw new BuildException("Basedir " . $dir->getAbsolutePath() . " is not a directory");
        }
        $this->basedir = $dir;
        $this->setPropertyInternal("project.basedir", $this->basedir->getAbsolutePath());
        $this->log("Project base dir set to: " . $this->basedir->getPath(), Project::MSG_VERBOSE);

        // [HL] added this so that ./ files resolve correctly.  This may be a mistake ... or may be in wrong place.
        chdir($dir->getAbsolutePath());
    }

    /**
     * Returns the basedir of this project
     *
     * @return PhingFile      Basedir PhingFile object
     *
     * @throws BuildException
     *
     * @author  Andreas Aderhold, andi@binarycloud.com
     */
    public function getBasedir()
    {
        if ($this->basedir === null) {
            try { // try to set it
                $this->setBasedir(".");
            } catch (BuildException $exc) {
                throw new BuildException("Can not set default basedir. " . $exc->getMessage());
            }
        }

        return $this->basedir;
    }

    /**
     * Set &quot;keep-going&quot; mode. In this mode Ant will try to execute
     * as many targets as possible. All targets that do not depend
     * on failed target(s) will be executed.  If the keepGoing settor/getter
     * methods are used in conjunction with the <code>ant.executor.class</code>
     * property, they will have no effect.
     * @param keepGoingMode &quot;keep-going&quot; mode
     */
    public function setKeepGoingMode($keepGoingMode)
    {
        $this->keepGoingMode = $keepGoingMode;
    }

    /**
     * Return the keep-going mode.  If the keepGoing settor/getter
     * methods are used in conjunction with the <code>phing.executor.class</code>
     * property, they will have no effect.
     * @return bool &quot;keep-going&quot; mode
     */
    public function isKeepGoingMode()
    {
        return $this->keepGoingMode;
    }

    /**
     * Sets system properties and the environment variables for this project.
     *
     * @return void
     */
    public function setSystemProperties()
    {

        // first get system properties
        $systemP = array_merge(self::getProperties(), Phing::getProperties());
        foreach ($systemP as $name => $value) {
            $this->setPropertyInternal($name, $value);
        }

        // and now the env vars
        foreach ($_SERVER as $name => $value) {
            // skip arrays
            if (is_array($value)) {
                continue;
            }
            $this->setPropertyInternal('env.' . $name, $value);
        }

        return true;
    }

    /**
     * Adds a task definition.
     * @param string $name      Name of tag.
     * @param string $class     The class path to use.
     * @param string $classpath The classpat to use.
     */
    public function addTaskDefinition($name, $class, $classpath = null)
    {
        if ($class === "") {
            $this->log("Task $name has no class defined.", Project::MSG_ERR);
        } elseif (!isset($this->taskdefs[$name])) {
            Phing::import($class, $classpath);
            $this->taskdefs[$name] = $class;
            $this->log("  +Task definition: $name ($class)", Project::MSG_DEBUG);
        } else {
            $this->log("Task $name ($class) already registered, skipping", Project::MSG_VERBOSE);
        }
    }

    /**
     * Returns the task definitions
     * @return array
     */
    public function getTaskDefinitions()
    {
        return $this->taskdefs;
    }

    /**
     * Adds a data type definition.
     * @param string $typeName  Name of the type.
     * @param string $typeClass The class to use.
     * @param string $classpath The classpath to use.
     */
    public function addDataTypeDefinition($typeName, $typeClass, $classpath = null)
    {
        if (!isset($this->typedefs[$typeName])) {
            Phing::import($typeClass, $classpath);
            $this->typedefs[$typeName] = $typeClass;
            $this->log("  +User datatype: $typeName ($typeClass)", Project::MSG_DEBUG);
        } else {
            $this->log("Type $typeName ($typeClass) already registered, skipping", Project::MSG_VERBOSE);
        }
    }

    /**
     * Returns the data type definitions
     * @return array
     */
    public function getDataTypeDefinitions()
    {
        return $this->typedefs;
    }

    /**
     * Add a new target to the project
     * @param string $targetName
     * @param Target $target
     * @throws BuildException
     */
    public function addTarget($targetName, $target)
    {
        if (isset($this->targets[$targetName])) {
            throw new BuildException("Duplicate target: $targetName");
        }
        $this->addOrReplaceTarget($targetName, $target);
    }

    /**
     * Adds or replaces a target in the project
     * @param string $targetName
     * @param Target $target
     */
    public function addOrReplaceTarget($targetName, &$target)
    {
        $this->log("  +Target: $targetName", Project::MSG_DEBUG);
        $target->setProject($this);
        $this->targets[$targetName] = $target;

        $ctx = $this->getReference("phing.parsing.context");
        $current = $ctx->getCurrentTargets();
        $current[$targetName] = $target;
    }

    /**
     * Returns the available targets
     * @return array
     */
    public function getTargets()
    {
        return $this->targets;
    }

    /**
     * Create a new task instance and return reference to it. This method is
     * sorta factory like. A _local_ instance is created and a reference returned to
     * that instance. Usually PHP destroys local variables when the function call
     * ends. But not if you return a reference to that variable.
     * This is kinda error prone, because if no reference exists to the variable
     * it is destroyed just like leaving the local scope with primitive vars. There's no
     * central place where the instance is stored as in other OOP like languages.
     *
     * [HL] Well, ZE2 is here now, and this is  still working. We'll leave this alone
     * unless there's any good reason not to.
     *
     * @param  string         $taskType Task name
     * @return Task           A task object
     * @throws BuildException
     *                                 Exception
     */
    public function createTask($taskType)
    {
        try {
            $classname = "";
            $tasklwr = strtolower($taskType);
            foreach ($this->taskdefs as $name => $class) {
                if (strtolower($name) === $tasklwr) {
                    $classname = $class;
                    break;
                }
            }

            if ($classname === "") {
                return null;
            }

            $cls = Phing::import($classname);

            if (!class_exists($cls)) {
                throw new BuildException("Could not instantiate class $cls, even though a class was specified. (Make sure that the specified class file contains a class with the correct name.)");
            }

            $o = new $cls();

            if ($o instanceof Task) {
                $task = $o;
            } else {
                $this->log("  (Using TaskAdapter for: $taskType)", Project::MSG_DEBUG);
                // not a real task, try adapter
                $taskA = new TaskAdapter();
                $taskA->setProxy($o);
                $task = $taskA;
            }
            $task->setProject($this);
            $task->setTaskType($taskType);
            // set default value, can be changed by the user
            $task->setTaskName($taskType);
            $this->log("  +Task: " . $taskType, Project::MSG_DEBUG);
        } catch (Exception $t) {
            throw new BuildException("Could not create task of type: " . $taskType, $t);
        }
        // everything fine return reference
        return $task;
    }

    /**
     * Creates a new condition and returns the reference to it
     *
     * @param  string         $conditionType
     * @return Condition
     * @throws BuildException
     */
    public function createCondition($conditionType)
    {
        try {
            $classname = "";
            $tasklwr = strtolower($conditionType);
            foreach ($this->typedefs as $name => $class) {
                if (strtolower($name) === $tasklwr) {
                    $classname = $class;
                    break;
                }
            }

            if ($classname === "") {
                return null;
            }

            $cls = Phing::import($classname);

            if (!class_exists($cls)) {
                throw new BuildException("Could not instantiate class $cls, even though a class was specified. (Make sure that the specified class file contains a class with the correct name.)");
            }

            $o = new $cls();
            if ($o instanceof Condition) {
                return $o;
            } else {
                throw new BuildException("Not actually a condition");
            }
        } catch (Exception $e) {
            throw new BuildException("Could not create condition of type: " . $conditionType, $e);
        }
    }

    /**
     * Create a datatype instance and return reference to it
     * See createTask() for explanation how this works
     *
     * @param  string         $typeName Type name
     * @return object         A datatype object
     * @throws BuildException
     *                                 Exception
     */
    public function createDataType($typeName)
    {
        try {
            $cls = "";
            $typelwr = strtolower($typeName);
            foreach ($this->typedefs as $name => $class) {
                if (strtolower($name) === $typelwr) {
                    $cls = StringHelper::unqualify($class);
                    break;
                }
            }

            if ($cls === "") {
                return null;
            }

            if (!class_exists($cls)) {
                throw new BuildException("Could not instantiate class $cls, even though a class was specified. (Make sure that the specified class file contains a class with the correct name.)");
            }

            $type = new $cls();
            $this->log("  +Type: $typeName", Project::MSG_DEBUG);
            if (!($type instanceof DataType)) {
                throw new Exception("$class is not an instance of phing.types.DataType");
            }
            if ($type instanceof ProjectComponent) {
                $type->setProject($this);
            }
        } catch (Exception $t) {
            throw new BuildException("Could not create type: $typeName", $t);
        }
        // everything fine return reference
        return $type;
    }

    /**
     * Executes a list of targets
     *
     * @param  array          $targetNames List of target names to execute
     * @return void
     * @throws BuildException
     */
    public function executeTargets($targetNames)
    {
        foreach ($targetNames as $tname) {
            $this->executeTarget($tname);
        }
    }

    /**
     * Executes a target
     *
     * @param  string         $targetName Name of Target to execute
     * @return void
     * @throws BuildException
     */
    public function executeTarget($targetName)
    {

        // complain about executing void
        if ($targetName === null) {
            throw new BuildException("No target specified");
        }

        // invoke topological sort of the target tree and run all targets
        // until targetName occurs.
        $sortedTargets = $this->_topoSort($targetName, $this->targets);

        $curIndex = (int) 0;
        $curTarget = null;
        $thrownException = null;
        $buildException = null;
        do {
            try {
                $curTarget = $sortedTargets[$curIndex++];
                $curTarget->performTasks();
            } catch (BuildException $exc) {
                if (!($this->keepGoingMode)) {
                    throw $exc;
                }
                $thrownException = $exc;
            }
            if ($thrownException != null) {
                if ($thrownException instanceof BuildException) {
                    $this->log(
                        "Target '" . $curTarget->getName()
                        . "' failed with message '"
                        . $thrownException->getMessage() . "'.", Project::MSG_ERR);
                    // only the first build exception is reported
                    if ($buildException === null) {
                        $buildException = $thrownException;
                    }
                } else {
                    $this->log(
                        "Target '" . $curTarget->getName()
                        . "' failed with message '"
                        . $thrownException->getMessage() . "'." . PHP_EOL
                        . $thrownException->getTraceAsString(), Project::MSG_ERR
                    );
                    if ($buildException === null) {
                        $buildException = new BuildException($thrownException);
                    }
                }
            }
        } while ($curTarget->getName() !== $targetName);

        if ($buildException !== null) {
            throw $buildException;
        }
    }

    /**
     * Helper function
     * @param $fileName
     * @param null $rootDir
     * @throws IOException
     * @return \PhingFile
     */
    public function resolveFile($fileName, $rootDir = null)
    {
        if ($rootDir === null) {
            return $this->fileUtils->resolveFile($this->basedir, $fileName);
        } else {
            return $this->fileUtils->resolveFile($rootDir, $fileName);
        }
    }

    /**
     * Topologically sort a set of Targets.
     * @param  string $root is the (String) name of the root Target. The sort is
     *                         created in such a way that the sequence of Targets until the root
     *                         target is the minimum possible such sequence.
     * @param  array $targets is a array representing a "name to Target" mapping
     * @throws BuildException
     * @throws Exception
     * @return array of Strings with the names of the targets in
     *               sorted order.
     */
    public function _topoSort($root, &$targets)
    {

        $root = (string) $root;
        $ret = array();
        $state = array();
        $visiting = array();

        // We first run a DFS based sort using the root as the starting node.
        // This creates the minimum sequence of Targets to the root node.
        // We then do a sort on any remaining unVISITED targets.
        // This is unnecessary for doing our build, but it catches
        // circular dependencies or missing Targets on the entire
        // dependency tree, not just on the Targets that depend on the
        // build Target.

        $this->_tsort($root, $targets, $state, $visiting, $ret);

        $retHuman = "";
        for ($i = 0, $_i = count($ret); $i < $_i; $i++) {
            $retHuman .= $ret[$i]->toString() . " ";
        }
        $this->log("Build sequence for target '$root' is: $retHuman", Project::MSG_VERBOSE);

        $keys = array_keys($targets);
        while ($keys) {
            $curTargetName = (string) array_shift($keys);
            if (!isset($state[$curTargetName])) {
                $st = null;
            } else {
                $st = (string) $state[$curTargetName];
            }

            if ($st === null) {
                $this->_tsort($curTargetName, $targets, $state, $visiting, $ret);
            } elseif ($st === "VISITING") {
                throw new Exception("Unexpected node in visiting state: $curTargetName");
            }
        }

        $retHuman = "";
        for ($i = 0, $_i = count($ret); $i < $_i; $i++) {
            $retHuman .= $ret[$i]->toString() . " ";
        }
        $this->log("Complete build sequence is: $retHuman", Project::MSG_VERBOSE);

        return $ret;
    }

    // one step in a recursive DFS traversal of the target dependency tree.
    // - The array "state" contains the state (VISITED or VISITING or null)
    //   of all the target names.
    // - The stack "visiting" contains a stack of target names that are
    //   currently on the DFS stack. (NB: the target names in "visiting" are
    //    exactly the target names in "state" that are in the VISITING state.)
    // 1. Set the current target to the VISITING state, and push it onto
    //    the "visiting" stack.
    // 2. Throw a BuildException if any child of the current node is
    //    in the VISITING state (implies there is a cycle.) It uses the
    //    "visiting" Stack to construct the cycle.
    // 3. If any children have not been VISITED, tsort() the child.
    // 4. Add the current target to the Vector "ret" after the children
    //    have been visited. Move the current target to the VISITED state.
    //    "ret" now contains the sorted sequence of Targets up to the current
    //    Target.

    /**
     * @param $root
     * @param $targets
     * @param $state
     * @param $visiting
     * @param $ret
     * @throws BuildException
     * @throws Exception
     */
    public function _tsort($root, &$targets, &$state, &$visiting, &$ret)
    {
        $state[$root] = "VISITING";
        $visiting[] = $root;

        if (!isset($targets[$root]) || !($targets[$root] instanceof Target)) {
            $target = null;
        } else {
            $target = $targets[$root];
        }

        // make sure we exist
        if ($target === null) {
            $sb = "Target '$root' does not exist in this project.";
            array_pop($visiting);
            if (!empty($visiting)) {
                $parent = (string) $visiting[count($visiting) - 1];
                $sb .= " It is a dependency of target '$parent'.";
            }
            throw new BuildException($sb);
        }

        $deps = $target->getDependencies();

        while ($deps) {
            $cur = (string) array_shift($deps);
            if (!isset($state[$cur])) {
                $m = null;
            } else {
                $m = (string) $state[$cur];
            }
            if ($m === null) {
                // not been visited
                $this->_tsort($cur, $targets, $state, $visiting, $ret);
            } elseif ($m == "VISITING") {
                // currently visiting this node, so have a cycle
                throw $this->_makeCircularException($cur, $visiting);
            }
        }

        $p = (string) array_pop($visiting);
        if ($root !== $p) {
            throw new Exception("Unexpected internal error: expected to pop $root but got $p");
        }

        $state[$root] = "VISITED";
        $ret[] = $target;
    }

    /**
     * @param string $end
     * @param array $stk
     * @return BuildException
     */
    public function _makeCircularException($end, $stk)
    {
        $sb = "Circular dependency: $end";
        do {
            $c = (string) array_pop($stk);
            $sb .= " <- " . $c;
        } while ($c != $end);

        return new BuildException($sb);
    }

    /**
     * Adds a reference to an object. This method is called when the parser
     * detects a id="foo" attribute. It passes the id as $name and a reference
     * to the object assigned to this id as $value
     * @param string $name
     * @param object $object
     */
    public function addReference($name, $object)
    {
        if (isset($this->references[$name])) {
            $this->log("Overriding previous definition of reference to $name", Project::MSG_VERBOSE);
        }
        $this->log("Adding reference: $name -> " . get_class($object), Project::MSG_DEBUG);
        $this->references[$name] = $object;
    }

    /**
     * Returns the references array.
     * @return array
     */
    public function getReferences()
    {
        return $this->references;
    }

    /**
     * Returns a specific reference.
     * @param  string $key The reference id/key.
     * @return object Reference or null if not defined
     */
    public function getReference($key)
    {
        if (isset($this->references[$key])) {
            return $this->references[$key];
        }

        return null; // just to be explicit
    }

    /**
     * Abstracting and simplifyling Logger calls for project messages
     * @param string $msg
     * @param int    $level
     */
    public function log($msg, $level = Project::MSG_INFO)
    {
        $this->logObject($this, $msg, $level);
    }

    /**
     * @param $obj
     * @param $msg
     * @param $level
     */
    public function logObject($obj, $msg, $level)
    {
        $this->fireMessageLogged($obj, $msg, $level);
    }

    /**
     * @param BuildListener $listener
     */
    public function addBuildListener(BuildListener $listener)
    {
        $this->listeners[] = $listener;
    }

    /**
     * @param BuildListener $listener
     */
    public function removeBuildListener(BuildListener $listener)
    {
        $newarray = array();
        for ($i = 0, $size = count($this->listeners); $i < $size; $i++) {
            if ($this->listeners[$i] !== $listener) {
                $newarray[] = $this->listeners[$i];
            }
        }
        $this->listeners = $newarray;
    }

    /**
     * @return array
     */
    public function getBuildListeners()
    {
        return $this->listeners;
    }

    public function fireBuildStarted()
    {
        $event = new BuildEvent($this);
        foreach ($this->listeners as $listener) {
            $listener->buildStarted($event);
        }
    }

    /**
     * @param Exception $exception
     */
    public function fireBuildFinished($exception)
    {
        $event = new BuildEvent($this);
        $event->setException($exception);
        foreach ($this->listeners as $listener) {
            $listener->buildFinished($event);
        }
    }

    /**
     * @param $target
     */
    public function fireTargetStarted($target)
    {
        $event = new BuildEvent($target);
        foreach ($this->listeners as $listener) {
            $listener->targetStarted($event);
        }
    }

    /**
     * @param $target
     * @param $exception
     */
    public function fireTargetFinished($target, $exception)
    {
        $event = new BuildEvent($target);
        $event->setException($exception);
        foreach ($this->listeners as $listener) {
            $listener->targetFinished($event);
        }
    }

    /**
     * @param $task
     */
    public function fireTaskStarted($task)
    {
        $event = new BuildEvent($task);
        foreach ($this->listeners as $listener) {
            $listener->taskStarted($event);
        }
    }

    /**
     * @param $task
     * @param $exception
     */
    public function fireTaskFinished($task, $exception)
    {
        $event = new BuildEvent($task);
        $event->setException($exception);
        foreach ($this->listeners as $listener) {
            $listener->taskFinished($event);
        }
    }

    /**
     * @param $event
     * @param $message
     * @param $priority
     */
    public function fireMessageLoggedEvent($event, $message, $priority)
    {
        $event->setMessage($message, $priority);
        foreach ($this->listeners as $listener) {
            $listener->messageLogged($event);
        }
    }

    /**
     * @param $object
     * @param $message
     * @param $priority
     */
    public function fireMessageLogged($object, $message, $priority)
    {
        $this->fireMessageLoggedEvent(new BuildEvent($object), $message, $priority);
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 *  Abstract class providing properties and methods common to all
 *  the project components
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @author    Hans Lellelid <hans@xmpl.org>
 *
 * @package   phing
 */
abstract class ProjectComponent
{
    /**
     * Holds a reference to the project that a project component
     * (a task, a target, etc.) belongs to
     *
     * @var Project $project A reference to the current project instance
     */
    protected $project = null;

    /**
     * References the project to the current component.
     *
     * @param Project $project The reference to the current project
     *
     * @return void
     */
    public function setProject($project)
    {
        $this->project = $project;
    }

    /**
     * Returns a reference to current project
     *
     * @return Project Reference to current porject object
     */
    public function getProject()
    {
        return $this->project;
    }

    /**
     * Logs a message with the given priority.
     *
     * @param string  $msg   The message to be logged.
     * @param integer $level The message's priority at this message should have
     *
     * @return void
     */
    public function log($msg, $level = Project::MSG_INFO)
    {
        if ($this->project !== null) {
            $this->project->log($msg, $level);
        }
    }
}
<?php
/*
 *  $Id: 465efa4e9b1232a9ede67d3a3adadbe1fbafb7e2 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 *  Wrapper class that holds the attributes of a Task (or elements
 *  nested below that level) and takes care of configuring that element
 *  at runtime.
 *
 *  <strong>SMART-UP INLINE DOCS</strong>
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: 465efa4e9b1232a9ede67d3a3adadbe1fbafb7e2 $
 * @package   phing
 */
class RuntimeConfigurable
{
    private $elementTag = null;

    /** @var array $children */
    private $children = array();

    /** @var object|Task $wrappedObject */
    private $wrappedObject = null;

    /** @var array $attributes */
    private $attributes = array();

    /** @var string $characters */
    private $characters = "";

    /** @var bool $proxyConfigured */
    private $proxyConfigured = false;

    /**
     * @param Task|object $proxy
     * @param mixed $elementTag The element to wrap.
     */
    public function __construct($proxy, $elementTag)
    {
        $this->wrappedObject = $proxy;
        $this->elementTag = $elementTag;

        if ($proxy instanceof Task) {
            $proxy->setRuntimeConfigurableWrapper($this);
        }
    }

    /**
     * @return object|Task
     */
    public function getProxy()
    {
        return $this->wrappedObject;
    }

    /**
     * @param object|Task $proxy
     *
     * @return void
     */
    public function setProxy($proxy)
    {
        $this->wrappedObject = $proxy;
        $this->proxyConfigured = false;
    }

    /**
     * Set's the attributes for the wrapped element.
     *
     * @param array $attributes
     *
     * @return void
     */
    public function setAttributes($attributes)
    {
        $this->attributes = $attributes;
    }

    /**
     * Returns the AttributeList of the wrapped element.
     *
     * @return array
     */
    public function getAttributes()
    {
        return $this->attributes;
    }

    /**
     * Adds child elements to the wrapped element.
     *
     * @param RuntimeConfigurable $child
     *
     * @return void
     */
    public function addChild(RuntimeConfigurable $child)
    {
        $this->children[] = $child;
    }

    /**
     * Returns the child with index
     *
     * @param int $index
     *
     * @return RuntimeConfigurable
     */
    public function getChild($index)
    {
        return $this->children[(int) $index];
    }

    /**
     * Add characters from #PCDATA areas to the wrapped element.
     *
     * @param string $data
     *
     * @return void
     */
    public function addText($data)
    {
        $this->characters .= (string) $data;
    }

    public function getElementTag()
    {
        return $this->elementTag;
    }

    /**
     * Configure the wrapped element and all children.
     *
     * @param Project $project
     *
     * @return void
     *
     * @throws BuildException
     * @throws Exception
     */
    public function maybeConfigure(Project $project)
    {
        if ($this->proxyConfigured) {
            return;
        }

        $id = null;

        // DataType configured in ProjectConfigurator
        //        if ( is_a($this->wrappedObject, "DataType") )
        //            return;

        if ($this->attributes || (isset($this->characters) && $this->characters != '')) {
            ProjectConfigurator::configure($this->wrappedObject, $this->attributes, $project);

            if (isset($this->attributes["id"])) {
                $id = $this->attributes["id"];
            }

            if (isset($this->characters) && $this->characters != '') {
                ProjectConfigurator::addText($project, $this->wrappedObject, (string) $this->characters);
            }
            if ($id !== null) {
                $project->addReference($id, $this->wrappedObject);
            }
        }

        /*if ( is_array($this->children) && !empty($this->children) ) {
            // Configure all child of this object ...
            foreach ($this->children as $child) {
                $child->maybeConfigure($project);
                ProjectConfigurator::storeChild($project, $this->wrappedObject, $child->wrappedObject, strtolower($child->getElementTag()));
            }
        }*/

        $this->proxyConfigured = true;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/BuildListener.php';

/**
 * Instances of classes that implement this interface can register
 * to be also notified when things happened during a subbuild.
 *
 * <p>A subbuild is a separate project instance created by the
 * <code>&lt;phing&gt;</code> task family.  These project instances will
 * never fire the buildStarted and buildFinished events, but they will
 * fire subBuildStarted/ and subBuildFinished.  The main project
 * instance - the one created by running Phing in the first place - will
 * never invoke one of the methods of this interface.</p>
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing
 */
interface SubBuildListener extends BuildListener
{
    /**
     * Signals that a subbuild has started. This event
     * is fired before any targets have started.
     *
     * @param BuildEvent $event An event with any relevant extra information.
     *                          Must not be <code>null</code>.
     */
    public function subBuildStarted(BuildEvent $event);

    /**
     * Signals that the last target has finished. This event
     * will still be fired if an error occurred during the build.
     *
     * @param BuildEvent $event An event with any relevant extra information.
     *                          Must not be <code>null</code>.
     *
     * @see BuildEvent::getException()
     */
    public function subBuildFinished(BuildEvent $event);
}
<?php
/*
 *  $Id: 860064af39647023c2644e87267184447ceb93ae $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/system/io/Reader.php';

/**
 * Convenience class for reading files.
 *
 * @author    <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a>
 *
 * @see       FilterReader
 *
 * @package   phing.system.io
 */
class BufferedReader extends Reader
{

    private $bufferSize = 0;
    private $buffer = null;
    private $bufferPos = 0;

    /**
     * The Reader we are buffering for.
     */
    private $in;

    /**
     *
     * @param Reader  $reader   The reader (e.g. FileReader).
     * @param integer $buffsize The size of the buffer we should use for reading files.
     *                          A large buffer ensures that most files (all scripts?) are parsed in 1 buffer.
     */
    public function __construct(Reader $reader, $buffsize = 65536)
    {
        $this->in = $reader;
        $this->bufferSize = $buffsize;
    }

    /**
     * Reads and returns a chunk of data.
     * @param  int   $len Number of bytes to read.  Default is to read configured buffer size number of bytes.
     * @return mixed buffer or -1 if EOF.
     */
    public function read($len = null)
    {

        // if $len is specified, we'll use that; otherwise, use the configured buffer size.
        if ($len === null) {
            $len = $this->bufferSize;
        }

        if (($data = $this->in->read($len)) !== -1) {

            // not all files end with a newline character, so we also need to check EOF
            if (!$this->in->eof()) {

                $notValidPart = strrchr($data, "\n");
                $notValidPartSize = strlen($notValidPart);

                if ($notValidPartSize > 1) {
                    // Block doesn't finish on a EOL
                    // Find the last EOL and forget all following stuff
                    $dataSize = strlen($data);
                    $validSize = $dataSize - $notValidPartSize + 1;

                    $data = substr($data, 0, $validSize);

                    // Rewind to the beginning of the forgotten stuff.
                    $this->in->skip(-$notValidPartSize + 1);
                }

            } // if !EOF
        }

        return $data;
    }

    /**
     * @param int $n
     */
    public function skip($n)
    {
        return $this->in->skip($n);
    }

    public function reset()
    {
        return $this->in->reset();
    }

    /**
     * @return mixed
     */
    public function close()
    {
        return $this->in->close();
    }

    /**
     * @return mixed
     */
    public function open()
    {
        return $this->in->open();
    }

    /**
     * Read a line from input stream.
     */
    public function readLine()
    {
        $line = null;
        while (($ch = $this->readChar()) !== -1) {
            if ($ch === "\n") {
                break;
            }
            $line .= $ch;
        }

        // Warning : Not considering an empty line as an EOF
        if ($line === null && $ch !== -1) {
            return "";
        }

        return $line;
    }

    /**
     * Reads a single char from the reader.
     * @return string single char or -1 if EOF.
     */
    public function readChar()
    {

        if ($this->buffer === null) {
            // Buffer is empty, fill it ...
            $read = $this->in->read($this->bufferSize);
            if ($read === -1) {
                $ch = -1;
            } else {
                $this->buffer = $read;

                return $this->readChar(); // recurse
            }
        } else {
            // Get next buffered char ...
            // handle case where buffer is read-in, but is empty.  The next readChar() will return -1 EOF,
            // so we just return empty string (char) at this point.  (Probably could also return -1 ...?)
            $ch = ($this->buffer !== "") ? $this->buffer{$this->bufferPos} : '';
            $this->bufferPos++;
            if ($this->bufferPos >= strlen($this->buffer)) {
                $this->buffer = null;
                $this->bufferPos = 0;
            }
        }

        return $ch;
    }

    /**
     * Returns whether eof has been reached in stream.
     * This is important, because filters may want to know if the end of the file (and not just buffer)
     * has been reached.
     * @return boolean
     */
    public function eof()
    {
        return $this->in->eof();
    }

    /**
     * @return string
     */
    public function getResource()
    {
        return $this->in->getResource();
    }
}
<?php
/*
 *  $Id: 4ee126846fc7a26f86eb5ad68012fb4eff878d57 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/system/io/Writer.php';

/**
 * Convenience class for writing files.
 *
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: 4ee126846fc7a26f86eb5ad68012fb4eff878d57 $
 * @package   phing.system.io
 */
class BufferedWriter extends Writer
{

    /**
     * The size of the buffer in kb.
     */
    private $bufferSize = 0;

    /**
     * @var Writer The Writer we are buffering output to.
     */
    private $out;

    /**
     * @param Writer $writer
     * @param int $buffsize
     */
    public function __construct(Writer $writer, $buffsize = 8192)
    {
        $this->out = $writer;
        $this->bufferSize = $buffsize;
    }

    /**
     * @param string $buf
     * @param int $off
     * @param int $len
     * @return mixed
     */
    public function write($buf, $off = null, $len = null)
    {
        return $this->out->write($buf, $off, $len);
    }

    public function newLine()
    {
        $this->write(PHP_EOL);
    }

    /**
     * @return string
     */
    public function getResource()
    {
        return $this->out->getResource();
    }

    public function flush()
    {
        $this->out->flush();
    }

    /**
     * Close attached stream.
     */
    public function close()
    {
        return $this->out->close();
    }

}
<?php
/*
 *  $Id: 398bedf237ea340a7a02d4c77a480a4cd7a34e0c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/system/io/Reader.php';

/**
 * Convenience class for reading console input.
 *
 * @author Hans Lellelid <hans@xmpl.org>
 * @author Matthew Hershberger <matthewh@lightsp.com>
 * @version $Id: 398bedf237ea340a7a02d4c77a480a4cd7a34e0c $
 * @package phing.system.io
 */
class ConsoleReader extends Reader
{

    /**
     * @return string
     */
    public function readLine()
    {

        $out = fgets(STDIN); // note: default maxlen is 1kb
        $out = rtrim($out);

        return $out;
    }

    /**
     *
     * @param  int    $len Num chars to read.
     * @return string chars read or -1 if eof.
     */
    public function read($len = null)
    {

        $out = fread(STDIN, $len);

        return $out;
        // FIXME
        // read by chars doesn't work (yet?) with PHP stdin.  Maybe
        // this is just a language feature, maybe there's a way to get
        // ability to read chars w/o <enter> ?

    }

    public function close()
    {
        // STDIN is always open
    }

    public function open()
    {
        // STDIN is always open
    }

    /**
     * Whether eof has been reached with stream.
     * @return boolean
     */
    public function eof()
    {
        return feof(STDIN);
    }

    /**
     * Returns path to file we are reading.
     * @return string
     */
    public function getResource()
    {
        return "console";
    }
}
<?php
/*
 *  $Id: 0e41dd4805552f11260977c86fbbc05fa5b57f54 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/InputStream.php';
require_once 'phing/system/io/PhingFile.php';

/**
 * Input stream subclass for file streams.
 *
 * @package   phing.system.io
 */
class FileInputStream extends InputStream
{

    /**
     * The associated file.
     * @var PhingFile
     */
    protected $file;

    /**
     * Construct a new FileInputStream.
     *
     * @param  PhingFile|string $file   Path to the file
     * @param  boolean          $append Whether to append (ignored)
     * @throws Exception        - if invalid argument specified.
     * @throws IOException      - if unable to open file.
     */
    public function __construct($file, $append = false)
    {
        if ($file instanceof PhingFile) {
            $this->file = $file;
        } elseif (is_string($file)) {
            $this->file = new PhingFile($file);
        } else {
            throw new Exception("Invalid argument type for \$file.");
        }

        if (!$this->file->exists()) {
            throw new IOException("Unable to open " . $this->file->__toString() . " for reading. File does not exists.");
        }
        if (!$this->file->canRead()) {
            throw new IOException("Unable to open " . $this->file->__toString() . " for reading. File not readable.");
        }
        $stream = @fopen($this->file->getAbsolutePath(), "rb");
        if ($stream === false) {
            throw new IOException("Unable to open " . $this->file->__toString() . " for reading: " . print_r(error_get_last(), true));
        }

        parent::__construct($stream);
    }

    /**
     * Returns a string representation of the attached file.
     * @return string
     */
    public function __toString()
    {
        return $this->file->getPath();
    }

    /**
     * Mark is supported by FileInputStream.
     * @return boolean TRUE
     */
    public function markSupported()
    {
        return true;
    }
}
<?php
/*
 *  $Id: 4c7e9ad51a553d391c01cd7df08f4638279b620d $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/OutputStream.php';
require_once 'phing/system/io/PhingFile.php';

/**
 * Output stream subclass for file streams.
 *
 * @package   phing.system.io
 */
class FileOutputStream extends OutputStream
{

    /**
     * @var PhingFile The associated file.
     */
    protected $file;

    /**
     * Construct a new FileOutputStream.
     * @param  mixed       $file
     * @param  boolean     $append Whether to append bytes to end of file rather than beginning.
     * @throws Exception   - if invalid argument specified.
     * @throws IOException - if unable to open file.
     */
    public function __construct($file, $append = false)
    {
        global $php_errormsg;
        if ($file instanceof PhingFile) {
            $this->file = $file;
        } elseif (is_string($file)) {
            $this->file = new PhingFile($file);
        } else {
            throw new Exception("Invalid argument type for \$file.");
        }
        if ($append) {
            $stream = @fopen($this->file->getAbsolutePath(), "ab");
        } else {
            $stream = @fopen($this->file->getAbsolutePath(), "wb");
        }
        if ($stream === false) {
            throw new IOException("Unable to open " . $this->file->__toString() . " for writing: " . $php_errormsg);
        }
        parent::__construct($stream);
    }

    /**
     * Returns a string representation of the attached file.
     * @return string
     */
    public function __toString()
    {
        return $this->file->getPath();
    }
}
<?php
/*
 *  $Id: 43943059b164e043e0426abf4f1365cf17b7c5c1 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */
include_once 'phing/system/io/IniFileParser.php';
include_once 'phing/system/io/FileParserFactoryInterface.php';
include_once 'phing/system/io/YamlFileParser.php';

/**
 * The factory to create fileParsers based on extension name from
 * PhingFile->getFileExtension()
 *
 * @author Mike Lohmann <mike.lohmann@deck36.de>
 * @package phing.system.io
 */
class FileParserFactory implements FileParserFactoryInterface
{
    /**
     * @const string
     */
    const YAMLFILEEXTENSION = 'yml';

    /**
   * @const string
   */
    const YAMLFILEEXTENSIONLONG = 'yaml';

    /**
     * {@inheritDoc}
     */
    public function createParser($fileExtension)
    {
        if (phpversion() >= 5.3) {
            switch ($fileExtension) {
                case self::YAMLFILEEXTENSION:
                case self::YAMLFILEEXTENSIONLONG:
                    $fileParser = new YamlFileParser();
                    break;
                default:
                    $fileParser = new IniFileParser();
            }
        } else {
            $fileParser = new IniFileParser();
        }

        return $fileParser;
    }
}
<?php
/*
 *  $Id: e45c62e8c6f1ce0fd9ef5cb8ade9341e5f85207b $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * This interface can be used to implement a fileParserFactory to create FileParsers based on config.
 * Currently it is used in the PropertyTask to deliver the correct parser based on filetype.
 *
 * @author Mike Lohmann <mike.lohmann@deck36.de>
 * @package phing.system.io
 */
interface FileParserFactoryInterface
{
    /**
     * Based on the $name a parser will be returned.
     *
     * @param string $fileExtension
     * @return FileParserInterface
     */
    public function createParser($fileExtension);
}
<?php
/*
 *  $Id: e6a3494ac479b4dd958c3fa1558bdd7e9fef3708 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * This interface can be used to implement a fileParser for property files.
 * For example: You can implement a .ini-Fileparser or .yaml/.xml/.php.
 *
 * @author Mike Lohmann <mike.lohmann@deck36.de>
 * @package phing.system.io
 */
interface FileParserInterface
{
    /**
     * Builds an array from the given (ini) file and returns it.
     *
     * @param $file
     * @return array
     * @throws IOException
     */
    public function parseFile(PhingFile $file);
}
<?php
/*
 *  $Id: 61dccc656f72e8538b560c3f4e69cdab8047f83d $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/InputStreamReader.php';
require_once 'phing/system/io/FileInputStream.php';

/**
 * Convenience class for reading files.
 * @package   phing.system.io
 */
class FileReader extends InputStreamReader
{

    /**
     * Construct a new FileReader.
     * @param mixed $file PhingFile or string pathname.
     */
    public function __construct($file)
    {
        $in = new FileInputStream($file);
        parent::__construct($in);
    }

}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * This is an abstract class for platform specific filesystem implementations
 * you have to implement each method in the platform specific filesystem implementation
 * classes Your local filesytem implementation must extend this class.
 * You should also use this class as a template to write your local implementation
 * Some native PHP filesystem specific methods are abstracted here as well. Anyway
 * you _must_ always use this methods via a PhingFile object (that by nature uses the
 * *FileSystem drivers to access the real filesystem via this class using natives.
 *
 * FIXME:
 *  - Error handling reduced to min fallthrough runtime exceptions
 *    more precise errorhandling is done by the PhingFile class
 *
 * @author Charlie Killian <charlie@tizac.com>
 * @author Hans Lellelid <hans@xmpl.org>
 *
 * @package phing.system.io
 */
abstract class FileSystem
{

    /**
     * @var int
     */
    const BA_EXISTS = 0x01;

    /**
     * @var int
     */
    const BA_REGULAR = 0x02;

    /**
     * @var int
     */
    const BA_DIRECTORY = 0x04;

    /**
     * @var int
     */
    const BA_HIDDEN = 0x08;

    /**
     * Instance for getFileSystem() method.
     * @var FileSystem
     */
    private static $fs;

    /**
     * Static method to return the FileSystem singelton representing
     * this platform's local filesystem driver.
     *
     * @return FileSystem
     * @throws IOException
     */
    public static function getFileSystem()
    {
        if (self::$fs === null) {
            switch (Phing::getProperty('host.fstype')) {
                case 'UNIX':
                    include_once 'phing/system/io/UnixFileSystem.php';
                    self::$fs = new UnixFileSystem();
                    break;
                case 'WIN32':
                    include_once 'phing/system/io/Win32FileSystem.php';
                    self::$fs = new Win32FileSystem();
                    break;
                case 'WINNT':
                    include_once 'phing/system/io/WinNTFileSystem.php';
                    self::$fs = new WinNTFileSystem();
                    break;
                default:
                    throw new IOException("Host uses unsupported filesystem, unable to proceed");
            }
        }

        return self::$fs;
    }

    /* -- Normalization and construction -- */

    /**
     * Return the local filesystem's name-separator character.
     */
    abstract public function getSeparator();

    /**
     * Return the local filesystem's path-separator character.
     */
    abstract public function getPathSeparator();

    /**
     * Convert the given pathname string to normal form.  If the string is
     * already in normal form then it is simply returned.
     *
     * @param string $strPath
     */
    abstract public function normalize($strPath);

    /**
     * Compute the length of this pathname string's prefix.  The pathname
     * string must be in normal form.
     *
     * @param string $pathname
     */
    abstract public function prefixLength($pathname);

    /**
     * Resolve the child pathname string against the parent.
     * Both strings must be in normal form, and the result
     * will be a string in normal form.
     *
     * @param string $parent
     * @param string $child
     */
    abstract public function resolve($parent, $child);

    /**
     * Resolve the given abstract pathname into absolute form.  Invoked by the
     * getAbsolutePath and getCanonicalPath methods in the PhingFile class.
     *
     * @param PhingFile $f
     */
    abstract public function resolveFile(PhingFile $f);

    /**
     * Return the parent pathname string to be used when the parent-directory
     * argument in one of the two-argument PhingFile constructors is the empty
     * pathname.
     */
    abstract public function getDefaultParent();

    /**
     * Post-process the given URI path string if necessary.  This is used on
     * win32, e.g., to transform "/c:/foo" into "c:/foo".  The path string
     * still has slash separators; code in the PhingFile class will translate them
     * after this method returns.
     *
     * @param string $path
     */
    abstract public function fromURIPath($path);

    /* -- Path operations -- */

    /**
     * Tell whether or not the given abstract pathname is absolute.
     *
     * @param PhingFile $f
     */
    abstract public function isAbsolute(PhingFile $f);

    /**
     * canonicalize filename by checking on disk
     * @param  string $strPath
     * @return mixed  Canonical path or false if the file doesn't exist.
     */
    public function canonicalize($strPath)
    {
        return @realpath($strPath);
    }

    /* -- Attribute accessors -- */

    /**
     * Return the simple boolean attributes for the file or directory denoted
     * by the given abstract pathname, or zero if it does not exist or some
     * other I/O error occurs.
     *
     * @param PhingFile $f
     * @throws IOException
     */
    public function getBooleanAttributes($f)
    {
        throw new IOException("getBooleanAttributes() not implemented by fs driver");
    }

    /**
     * Check whether the file or directory denoted by the given abstract
     * pathname may be accessed by this process.  If the second argument is
     * false, then a check for read access is made; if the second
     * argument is true, then a check for write (not read-write)
     * access is made.  Return false if access is denied or an I/O error
     * occurs.
     *
     * @param PhingFile $f
     * @param boolean $write
     * @return bool
     */
    public function checkAccess(PhingFile $f, $write = false)
    {
        // we clear stat cache, its expensive to look up from scratch,
        // but we need to be sure
        @clearstatcache();


        // Shouldn't this be $f->GetAbsolutePath() ?
        // And why doesn't GetAbsolutePath() work?

        $strPath = (string) $f->getPath();

        // FIXME
        // if file object does denote a file that yet not existst
        // path rights are checked
        if (!@file_exists($strPath) && !is_dir($strPath)) {
            $strPath = $f->getParent();
            if ($strPath === null || !is_dir($strPath)) {
                $strPath = Phing::getProperty("user.dir");
            }
            //$strPath = dirname($strPath);
        }

        if (!$write) {
            return (boolean) @is_readable($strPath);
        } else {
            return (boolean) @is_writable($strPath);
        }
    }

    /**
     * Whether file can be deleted.
     * @param  PhingFile $f
     * @return boolean
     */
    public function canDelete(PhingFile $f)
    {
        clearstatcache();
        $dir = dirname($f->getAbsolutePath());

        return (bool) @is_writable($dir);
    }

    /**
     * Return the time at which the file or directory denoted by the given
     * abstract pathname was last modified, or zero if it does not exist or
     * some other I/O error occurs.
     *
     * @param  PhingFile   $f
     * @return int
     * @throws IOException
     */
    public function getLastModifiedTime(PhingFile $f)
    {

        if (!$f->exists()) {
            return 0;
        }

        @clearstatcache();
        $strPath = (string) $f->getPath();

        if (@is_link($strPath)) {
            $stats = @lstat($strPath);

            if (!isset($stats['mtime'])) {
                $mtime = false;
            } else {
                $mtime = $stats['mtime'];
            }
        } else {
            $mtime = @filemtime($strPath);
        }

        if (false === $mtime) {
            $msg = "FileSystem::getLastModifiedTime() FAILED. Can not get modified time of $strPath. $php_errormsg";
            throw new IOException($msg);
        }

        return (int) $mtime;
    }

    /**
     * Return the length in bytes of the file denoted by the given abstract
     * pathname, or zero if it does not exist, is a directory, or some other
     * I/O error occurs.
     *
     * @param  PhingFile   $f
     * @throws IOException
     * @return int
     */
    public function getLength(PhingFile $f)
    {
        $strPath = (string) $f->getAbsolutePath();
        $fs = filesize((string) $strPath);
        if ($fs !== false) {
            return $fs;
        } else {
            $msg = "FileSystem::Read() FAILED. Cannot get filesize of $strPath. $php_errormsg";
            throw new IOException($msg);
        }
    }

    /* -- File operations -- */

    /**
     * Create a new empty file with the given pathname.  Return
     * true if the file was created and false if a
     * file or directory with the given pathname already exists.  Throw an
     * IOException if an I/O error occurs.
     *
     * @param  string      $strPathname Path of the file to be created.
     * @throws IOException
     * @return boolean
     */
    public function createNewFile($strPathname)
    {
        if (@file_exists($strPathname)) {
            return false;
        }

        // Create new file
        $fp = @fopen($strPathname, "w");
        if ($fp === false) {
            throw new IOException("The file \"$strPathname\" could not be created");
        }
        @fclose($fp);

        return true;
    }

    /**
     * Delete the file or directory denoted by the given abstract pathname,
     * returning true if and only if the operation succeeds.
     *
     * @param  PhingFile $f
     * @param  boolean   $recursive
     * @return void
     */
    public function delete(PhingFile $f, $recursive = false)
    {
        if ($f->isDirectory()) {
            return $this->rmdir($f->getPath(), $recursive);
        } else {
            return $this->unlink($f->getPath());
        }
    }

    /**
     * Arrange for the file or directory denoted by the given abstract
     * pathname to be deleted when Phing::shutdown is called, returning
     * true if and only if the operation succeeds.
     *
     * @param  PhingFile   $f
     * @throws IOException
     */
    public function deleteOnExit($f)
    {
        throw new IOException("deleteOnExit() not implemented by local fs driver");
    }

    /**
     * List the elements of the directory denoted by the given abstract
     * pathname.  Return an array of strings naming the elements of the
     * directory if successful; otherwise, return <code>null</code>.
     *
     * @param PhingFile $f
     * @return array
     */
    public function listDir(PhingFile $f)
    {
        $strPath = (string) $f->getAbsolutePath();
        $d = @dir($strPath);
        if (!$d) {
            return null;
        }
        $list = array();
        while ($entry = $d->read()) {
            if ($entry != "." && $entry != "..") {
                array_push($list, $entry);
            }
        }
        $d->close();
        unset($d);

        return $list;
    }

    /**
     * Create a new directory denoted by the given abstract pathname,
     * returning true if and only if the operation succeeds.
     *
     * NOTE: umask() is reset to 0 while executing mkdir(), and restored afterwards
     *
     * @param  PhingFile $f
     * @param  int       $mode
     * @return boolean
     */
    public function createDirectory(&$f, $mode = 0755)
    {
        $old_umask = umask(0);
        $return = @mkdir($f->getAbsolutePath(), $mode);
        umask($old_umask);

        return $return;
    }

    /**
     * Rename the file or directory denoted by the first abstract pathname to
     * the second abstract pathname, returning true if and only if
     * the operation succeeds.
     *
     * @param  PhingFile   $f1 abstract source file
     * @param  PhingFile   $f2 abstract destination file
     * @return void
     * @throws IOException if rename cannot be performed
     */
    public function rename(PhingFile $f1, PhingFile $f2)
    {
        // get the canonical paths of the file to rename
        $src = $f1->getAbsolutePath();
        $dest = $f2->getAbsolutePath();
        if (false === @rename($src, $dest)) {
            $msg = "Rename FAILED. Cannot rename $src to $dest. $php_errormsg";
            throw new IOException($msg);
        }
    }

    /**
     * Set the last-modified time of the file or directory denoted by the
     * given abstract pathname returning true if and only if the
     * operation succeeds.
     *
     * @param  PhingFile   $f
     * @param  int         $time
     * @return void
     * @throws IOException
     */
    public function setLastModifiedTime(PhingFile $f, $time)
    {
        $path = $f->getPath();
        $success = @touch($path, $time);
        if (!$success) {
            throw new IOException("Could not touch '" . $path . "' due to: $php_errormsg");
        }
    }

    /**
     * Mark the file or directory denoted by the given abstract pathname as
     * read-only, returning <code>true</code> if and only if the operation
     * succeeds.
     *
     * @param  PhingFile   $f
     * @throws IOException
     */
    public function setReadOnly($f)
    {
        throw new IOException("setReadonly() not implemented by local fs driver");
    }

    /* -- Filesystem interface -- */

    /**
     * List the available filesystem roots, return array of PhingFile objects
     * @throws IOException
     */
    public function listRoots()
    {
        throw new IOException("listRoots() not implemented by local fs driver");
    }

    /* -- Basic infrastructure -- */

    /**
     * Compare two abstract pathnames lexicographically.
     *
     * @param PhingFile $f1
     * @param PhingFile $f2
     * @throws IOException
     */
    public function compare(PhingFile $f1, PhingFile $f2)
    {
        throw new IOException("compare() not implemented by local fs driver");
    }

    /**
     * Copy a file.
     *
     * @param PhingFile $src  Source path and name file to copy.
     * @param PhingFile $dest Destination path and name of new file.
     *
     * @return void
     *
     * @throws IOException if file cannot be copied.
     */
    public function copy(PhingFile $src, PhingFile $dest)
    {
        global $php_errormsg;

        // Recursively copy a directory
        if ($src->isDirectory()) {
            return $this->copyr($src->getAbsolutePath(), $dest->getAbsolutePath());
        }

        $srcPath = $src->getAbsolutePath();
        $destPath = $dest->getAbsolutePath();

        if (false === @copy($srcPath, $destPath)) { // Copy FAILED. Log and return err.
            // Add error from php to end of log message. $php_errormsg.
            $msg = "FileSystem::copy() FAILED. Cannot copy $srcPath to $destPath. $php_errormsg";
            throw new IOException($msg);
        }

        $dest->setMode($src->getMode());
    }

    /**
     * Copy a file, or recursively copy a folder and its contents
     *
     * @author      Aidan Lister <aidan@php.net>
     * @version     1.0.1
     * @link        http://aidanlister.com/repos/v/function.copyr.php
     *
     * @param  string $source Source path
     * @param  string $dest   Destination path
     *
     * @return bool   Returns TRUE on success, FALSE on failure
     */
    public function copyr($source, $dest)
    {
        // Check for symlinks
        if (is_link($source)) {
            return symlink(readlink($source), $dest);
        }

        // Simple copy for a file
        if (is_file($source)) {
            return copy($source, $dest);
        }

        // Make destination directory
        if (!is_dir($dest)) {
            mkdir($dest);
        }

        // Loop through the folder
        $dir = dir($source);
        while (false !== $entry = $dir->read()) {
            // Skip pointers
            if ($entry == '.' || $entry == '..') {
                continue;
            }

            // Deep copy directories
            $this->copyr("$source/$entry", "$dest/$entry");
        }

        // Clean up
        $dir->close();

        return true;
    }

    /**
     * Change the ownership on a file or directory.
     *
     * @param string $pathname Path and name of file or directory.
     * @param string $user     The user name or number of the file or directory. See http://us.php.net/chown
     *
     * @return void
     *
     * @throws IOException if operation failed.
     */
    public function chown($pathname, $user)
    {
        if (false === @chown($pathname, $user)) { // FAILED.
            $msg = "FileSystem::chown() FAILED. Cannot chown $pathname. User $user." . (isset($php_errormsg) ? ' ' . $php_errormsg : "");
            throw new IOException($msg);
        }
    }

    /**
     * Change the group on a file or directory.
     *
     * @param string $pathname Path and name of file or directory.
     * @param string $group    The group of the file or directory. See http://us.php.net/chgrp
     *
     * @return void
     * @throws IOException if operation failed.
     */
    public function chgrp($pathname, $group)
    {
        if (false === @chgrp($pathname, $group)) { // FAILED.
            $msg = "FileSystem::chgrp() FAILED. Cannot chown $pathname. Group $group." . (isset($php_errormsg) ? ' ' . $php_errormsg : "");
            throw new IOException($msg);
        }
    }

    /**
     * Change the permissions on a file or directory.
     *
     * @param string $pathname Path and name of file or directory.
     * @param int    $mode     The mode (permissions) of the file or
     *                         directory. If using octal add leading 0. eg. 0777.
     *                         Mode is affected by the umask system setting.
     *
     * @return void
     * @throws IOException if operation failed.
     */
    public function chmod($pathname, $mode)
    {
        $str_mode = decoct($mode); // Show octal in messages.
        if (false === @chmod($pathname, $mode)) { // FAILED.
            $msg = "FileSystem::chmod() FAILED. Cannot chmod $pathname. Mode $str_mode." . (isset($php_errormsg) ? ' ' . $php_errormsg : "");
            throw new IOException($msg);
        }
    }

    /**
     * Locks a file and throws an Exception if this is not possible.
     *
     * @param  PhingFile $f
     * @return void
     * @throws IOException
     */
    public function lock(PhingFile $f)
    {
        $filename = $f->getPath();
        $fp = @fopen($filename, "w");
        $result = @flock($fp, LOCK_EX);
        @fclose($fp);
        if (!$result) {
            throw new IOException("Could not lock file '$filename'");
        }
    }

    /**
     * Unlocks a file and throws an IO Error if this is not possible.
     *
     * @param  PhingFile $f
     * @throws IOException
     * @return void
     */
    public function unlock(PhingFile $f)
    {
        $filename = $f->getPath();
        $fp = @fopen($filename, "w");
        $result = @flock($fp, LOCK_UN);
        fclose($fp);
        if (!$result) {
            throw new IOException("Could not unlock file '$filename'");
        }
    }

    /**
     * Delete a file.
     *
     * @param string $file Path and/or name of file to delete.
     *
     * @return void
     * @throws IOException - if an error is encountered.
     */
    public function unlink($file)
    {
        global $php_errormsg;
        if (false === @unlink($file)) {
            $msg = "FileSystem::unlink() FAILED. Cannot unlink '$file'. $php_errormsg";
            throw new IOException($msg);
        }
    }

    /**
     * Symbolically link a file to another name.
     *
     * Currently symlink is not implemented on Windows. Don't use if the application is to be portable.
     *
     * @param  string $target Path and/or name of file to link.
     * @param  string $link Path and/or name of link to be created.
     * @throws IOException
     * @return void
     */
    public function symlink($target, $link)
    {

        // If Windows OS then symlink() will report it is not supported in
        // the build. Use this error instead of checking for Windows as the OS.

        if (false === @symlink($target, $link)) {
            // Add error from php to end of log message. $php_errormsg.
            $msg = "FileSystem::Symlink() FAILED. Cannot symlink '$target' to '$link'. $php_errormsg";
            throw new IOException($msg);
        }

    }

    /**
     * Set the modification and access time on a file to the present time.
     *
     * @param  string $file Path and/or name of file to touch.
     * @param  int $time
     * @throws Exception
     * @return void
     */
    public function touch($file, $time = null)
    {
        global $php_errormsg;

        if (null === $time) {
            $error = @touch($file);
        } else {
            $error = @touch($file, $time);
        }

        if (false === $error) { // FAILED.
            // Add error from php to end of log message. $php_errormsg.
            $msg = "FileSystem::touch() FAILED. Cannot touch '$file'. $php_errormsg";
            throw new Exception($msg);
        }
    }

    /**
     * Delete an empty directory OR a directory and all of its contents.
     *
     * @param string $dir Path and/or name of directory to delete.
     * @param bool $children     False: don't delete directory contents.
     *                           True: delete directory contents.
     *
     * @throws Exception
     *
     * @return void
     */
    public function rmdir($dir, $children = false)
    {
        global $php_errormsg;

        // If children=FALSE only delete dir if empty.
        if (false === $children) {

            if (false === @rmdir($dir)) { // FAILED.
                // Add error from php to end of log message. $php_errormsg.
                $msg = "FileSystem::rmdir() FAILED. Cannot rmdir $dir. $php_errormsg";
                throw new Exception($msg);
            }

        } else { // delete contents and dir.

            $handle = @opendir($dir);

            if (false === $handle) { // Error.

                $msg = "FileSystem::rmdir() FAILED. Cannot opendir() $dir. $php_errormsg";
                throw new Exception($msg);

            } else { // Read from handle.

                // Don't error on readdir().
                while (false !== ($entry = @readdir($handle))) {

                    if ($entry != '.' && $entry != '..') {

                        // Only add / if it isn't already the last char.
                        // This ONLY serves the purpose of making the Logger
                        // output look nice:)

                        if (strpos(strrev($dir), DIRECTORY_SEPARATOR) === 0) { // there is a /
                            $next_entry = $dir . $entry;
                        } else { // no /
                            $next_entry = $dir . DIRECTORY_SEPARATOR . $entry;
                        }

                        // NOTE: As of php 4.1.1 is_dir doesn't return FALSE it
                        // returns 0. So use == not ===.

                        // Don't error on is_dir()
                        if (false == @is_dir($next_entry)) { // Is file.

                            try {
                                self::unlink($next_entry); // Delete.
                            } catch (Exception $e) {
                                $msg = "FileSystem::Rmdir() FAILED. Cannot FileSystem::Unlink() $next_entry. " . $e->getMessage(
                                    );
                                throw new Exception($msg);
                            }

                        } else { // Is directory.

                            try {
                                self::rmdir($next_entry, true); // Delete
                            } catch (Exception $e) {
                                $msg = "FileSystem::rmdir() FAILED. Cannot FileSystem::rmdir() $next_entry. " . $e->getMessage(
                                    );
                                throw new Exception($msg);
                            }

                        } // end is_dir else
                    } // end .. if
                } // end while
            } // end handle if

            // Don't error on closedir()
            @closedir($handle);

            if (false === @rmdir($dir)) { // FAILED.
                // Add error from php to end of log message. $php_errormsg.
                $msg = "FileSystem::rmdir() FAILED. Cannot rmdir $dir. $php_errormsg";
                throw new Exception($msg);
            }

        }

    }

    /**
     * Set the umask for file and directory creation.
     *
     * @param Int $mode
     * @throws Exception
     * @internal param Int $mode . Permissions usually in ocatal. Use leading 0 for
     *                    octal. Number between 0 and 0777.
     *
     * @return void
     */
    public function umask($mode)
    {
        global $php_errormsg;

        // CONSIDERME:
        // Throw a warning if mode is 0. PHP converts illegal octal numbers to
        // 0 so 0 might not be what the user intended.

        $str_mode = decoct($mode); // Show octal in messages.

        if (false === @umask($mode)) { // FAILED.
            // Add error from php to end of log message. $php_errormsg.
            $msg = "FileSystem::Umask() FAILED. Value $mode. $php_errormsg";
            throw new Exception($msg);
        }
    }

    /**
     * Compare the modified time of two files.
     *
     * @param string $file1 Path and name of file1.
     * @param string $file2 Path and name of file2.
     *
     * @return int  1 if file1 is newer.
     *              -1 if file2 is newer.
     *              0 if files have the same time.
     *              Err object on failure.
     *
     * @throws Exception - if cannot get modified time of either file.
     */
    public function compareMTimes($file1, $file2)
    {

        $mtime1 = filemtime($file1);
        $mtime2 = filemtime($file2);

        if ($mtime1 === false) { // FAILED. Log and return err.
            // Add error from php to end of log message. $php_errormsg.
            $msg = "FileSystem::compareMTimes() FAILED. Cannot can not get modified time of $file1.";
            throw new Exception($msg);
        } elseif ($mtime2 === false) { // FAILED. Log and return err.
            // Add error from php to end of log message. $php_errormsg.
            $msg = "FileSystem::compareMTimes() FAILED. Cannot can not get modified time of $file2.";
            throw new Exception($msg);
        } else { // Worked. Log and return compare.
            // Compare mtimes.
            if ($mtime1 == $mtime2) {
                return 0;
            } else {
                return ($mtime1 < $mtime2) ? -1 : 1;
            } // end compare
        }
    }
}
<?php
/*
 *  $Id: 8953d85899eaffc169ad01afc326fe4a960ffd91 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/OutputStreamWriter.php';
require_once 'phing/system/io/FileOutputStream.php';

/**
 * Convenience class for performing file write operations.
 *
 * @package   phing.system.io
 */
class FileWriter extends OutputStreamWriter
{

    /**
     * Construct a new FileWriter.
     * @param mixed   $file   PhingFile or string pathname.
     * @param boolean $append Append to existing file?
     */
    public function __construct($file, $append = false)
    {
        $out = new FileOutputStream($file, $append);
        parent::__construct($out);
    }
}
<?php
/*
 *  $Id: 8c0c1c8e5668e3d3c53912a61ee5003eff6e536c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/Reader.php';

/**
 * Wrapper class for readers, which can be used to apply filters.
 * @package phing.system.io
 */
class FilterReader extends Reader
{

    /**
     * @var Reader
     */
    protected $in;

    /**
     * @param Reader $in
     */
    public function __construct(Reader $in = null)
    {
        $this->in = $in;
    }

    /**
     * @param Reader $in
     */
    public function setReader(Reader $in)
    {
        $this->in = $in;
    }

    /**
     * @param int $n
     */
    public function skip($n)
    {
        return $this->in->skip($n);
    }

    /**
     * Read data from source.
     * FIXME: Clean up this function signature, as it a) params aren't being used
     * and b) it doesn't make much sense.
     * @param null $len
     * @return
     */
    public function read($len = null)
    {
        return $this->in->read($len);
    }

    public function reset()
    {
        return $this->in->reset();
    }

    public function close()
    {
        return $this->in->close();
    }

    /**
     * @return string
     */
    public function getResource()
    {
        return $this->in->getResource();
    }
}
<?php
/*
 *  $Id: 2611d65fac6d1b7d90d8d6ae76e4afbfe36c1d58 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */
include_once 'phing/system/io/FileParserInterface.php';

/**
 * Implements an IniFileParser. The logic is coming from th Properties.php, but I don't know who's the author.
 *
 * FIXME
 *  - Add support for arrays (separated by ',')
 *
 * @author Mike Lohmann <mike.lohmann@deck36.de>
 * @package phing.system.io
 */
class IniFileParser implements FileParserInterface
{
    /**
     * {@inheritDoc}
     */
    public function parseFile(PhingFile $file)
    {
        if (($lines = @file($file, FILE_IGNORE_NEW_LINES)) === false) {
            throw new IOException("Unable to parse contents of $file");
        }

        // concatenate lines ending with backslash
        $linesCount = count($lines);
        for ($i = 0; $i < $linesCount; $i++) {
            if (substr($lines[$i], -1, 1) === '\\') {
                $lines[$i + 1] = substr($lines[$i], 0, -1) . ltrim($lines[$i + 1]);
                $lines[$i] = '';
            }
        }

        $properties = array();
        foreach ($lines as $line) {
            // strip comments and leading/trailing spaces
            $line = trim(preg_replace("/\s+[;#]\s.+$/", "", $line));

            if (empty($line) || $line[0] == ';' || $line[0] == '#') {
                continue;
            }

            $pos = strpos($line, '=');
            $property = trim(substr($line, 0, $pos));
            $value = trim(substr($line, $pos + 1));
            $properties[$property] = $this->inVal($value);

        } // for each line

        return $properties;
    }

    /**
     * Process values when being read in from properties file.
     * does things like convert "true" => true
     * @param string $val Trimmed value.
     * @return mixed The new property value (may be boolean, etc.)
     */
    protected function inVal($val)
    {
        if ($val === "true") {
            $val = true;
        } elseif ($val === "false") {
            $val = false;
        }
        return $val;
    }
}
<?php
/*
 *  $Id: 65f63c742244bc32b89cc48618b9f6a38a37eea1 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Wrapper class for PHP stream that supports read operations.
 *
 * @package   phing.system.io
 */
class InputStream
{

    /**
     * @var resource The attached PHP stream.
     */
    protected $stream;

    /**
     * @var int Position of stream cursor.
     */
    protected $currentPosition = 0;

    /**
     * @var int Marked position of stream cursor.
     */
    protected $mark = 0;

    /**
     * Construct a new InputStream.
     * @param resource $stream Configured PHP stream for writing.
     * @throws IOException
     */
    public function __construct($stream)
    {
        if (!is_resource($stream)) {
            throw new IOException("Passed argument is not a valid stream.");
        }
        $this->stream = $stream;
    }

    /**
     * Skip over $n bytes.
     * @param int $n
     * @return int
     */
    public function skip($n)
    {
        $start = $this->currentPosition;

        $ret = @fseek($this->stream, $n, SEEK_CUR);
        if ($ret === -1) {
            return -1;
        }

        $this->currentPosition = ftell($this->stream);

        if ($start > $this->currentPosition) {
            $skipped = $start - $this->currentPosition;
        } else {
            $skipped = $this->currentPosition - $start;
        }

        return $skipped;
    }

    /**
     * Read data from stream until $len chars or EOF.
     * @param  int    $len Num chars to read.  If not specified this stream will read until EOF.
     * @return string chars read or -1 if eof.
     */
    public function read($len = null)
    {

        if ($this->eof()) {
            return -1;
        }

        if ($len === null) { // we want to keep reading until we get an eof
            $out = "";
            while (!$this->eof()) {
                $out .= fread($this->stream, 8192);
                $this->currentPosition = ftell($this->stream);
            }
        } else {
            $out = fread($this->stream, $len); // adding 1 seems to ensure that next call to read() will return EOF (-1)
            $this->currentPosition = ftell($this->stream);
        }

        return $out;
    }

    /**
     * Marks the current position in this input stream.
     * @throws IOException - if the underlying stream doesn't support this method.
     */
    public function mark()
    {
        if (!$this->markSupported()) {
            throw new IOException(get_class($this) . " does not support mark() and reset() methods.");
        }
        $this->mark = $this->currentPosition;
    }

    /**
     * Whether the input stream supports mark and reset methods.
     * @return boolean
     */
    public function markSupported()
    {
        return false;
    }

    /**
     * Repositions this stream to the position at the time the mark method was last called on this input stream.
     * @throws IOException - if the underlying stream doesn't support this method.
     */
    public function reset()
    {
        if (!$this->markSupported()) {
            throw new IOException(get_class($this) . " does not support mark() and reset() methods.");
        }
        // goes back to last mark, by default this would be 0 (i.e. rewind file).
        fseek($this->stream, SEEK_SET, $this->mark);
        $this->mark = 0;
    }

    /**
     * Closes stream.
     * @throws IOException if stream cannot be closed (note that calling close() on an already-closed stream will not raise an exception)
     */
    public function close()
    {
        if ($this->stream === null) {
            return;
        }
        if (false === @fclose($this->stream)) {
            // FAILED.
            $msg = "Cannot fclose " . $this->file->__toString() . " $php_errormsg";
            throw new IOException($msg);
        }
        $this->stream = null;
    }

    /**
     * Whether eof has been reached with stream.
     * @return boolean
     */
    public function eof()
    {
        return feof($this->stream);
    }

    /**
     * Reads a entire until EOF and places contents in passed-in variable.  Stream is closed after read.
     *
     * @param  string      &$rBuffer String variable where read contents will be put.
     * @return TRUE        on success.
     * @author  Charlie Killian, charlie@tizac.com
     * @throws IOException - if there is an error reading from stream.
     * @deprecated - Instead, use the read() method or a BufferedReader.
     */
    public function readInto(&$rBuffer)
    {
        $rBuffer = $this->read();
        $this->close();
    }

    /**
     * Returns string representation of attached stream.
     * @return string
     */
    public function __toString()
    {
        return (string) $this->stream;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/system/io/PhingFile.php';
include_once 'phing/system/io/Reader.php';

/**
 * Writer class for OutputStream objects.
 *
 * Unlike the Java counterpart, this class does not (yet) handle
 * character set transformations.  This will be an important function
 * of this class with move to supporting PHP6.
 *
 * @package   phing.system.io
 */
class InputStreamReader extends Reader
{

    /**
     * @var InputStream
     */
    protected $inStream;

    /**
     * Construct a new InputStreamReader.
     * @param InputStream $inStream
     * @internal param $InputStream $$inStream InputStream to read from
     */
    public function __construct(InputStream $inStream)
    {
        $this->inStream = $inStream;
    }

    /**
     * Close the stream.
     */
    public function close()
    {
        return $this->inStream->close();
    }

    /**
     * Skip over $n bytes.
     * @param int $n
     * @return int|void
     */
    public function skip($n)
    {
        return $this->inStream->skip($n);
    }

    /**
     * Read data from file.
     * @param  int    $len Num chars to read.
     * @return string chars read or -1 if eof.
     */
    public function read($len = null)
    {
        return $this->inStream->read($len);
    }

    /**
     * Marks the current position in this input stream.
     * @throws IOException - if the underlying stream doesn't support this method.
     */
    public function mark()
    {
        $this->inStream->mark();
    }

    /**
     * Whether the attached stream supports mark/reset.
     * @return boolean
     */
    public function markSupported()
    {
        return $this->inStream->markSupported();
    }

    /**
     * Repositions this stream to the position at the time the mark method was last called on this input stream.
     * @throws IOException - if the underlying stream doesn't support this method.
     */
    public function reset()
    {
        $this->inStream->reset();
    }

    /**
     * Whether eof has been reached with stream.
     * @return boolean
     */
    public function eof()
    {
        return $this->inStream->eof();
    }

    /**
     * Reads a entire file and stores the data in the variable
     * passed by reference.
     *
     * @param object &$rBuffer Reference. Variable of where to put contents.
     *
     * @return bool TRUE on success. Err object on failure.
     * @author  Charlie Killian, charlie@tizac.com
     * @deprecated Use read() or BufferedReader instead.
     */
    public function readInto(&$rBuffer)
    {
        return $this->inStream->readInto($rBuffer);
    }

    /**
     * Returns string representation of attached stream.
     * @return string
     */
    public function getResource()
    {
        return $this->inStream->__toString();
    }
}
<?php
/*
 *  $Id: 6332c0926e183bd18739ccc50aa58e4d79eb984f $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Extends Exception to take advantage of methods therein.
 *
 * @package   phing.system.io
 */
class IOException extends Exception
{
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Wrapper class for PHP stream that supports write operations.
 *
 * @package   phing.system.io
 */
class OutputStream
{
    /**
     * @var resource The configured PHP stream.
     */
    protected $stream;

    /**
     * Construct a new OutputStream.
     * @param resource $stream Configured PHP stream for writing.
     * @throws IOException
     */
    public function __construct($stream)
    {
        if (!is_resource($stream)) {
            throw new IOException("Passed argument is not a valid stream.");
        }
        $this->stream = $stream;
    }

    /**
     * Closes attached stream, flushing output first.
     * @throws IOException if cannot close stream (note that attempting to close an already closed stream will not raise an IOException)
     * @return void
     */
    public function close()
    {
        if ($this->stream === null) {
            return;
        }
        $this->flush();
        if (false === @fclose($this->stream)) {
            $metaData = stream_get_meta_data($this->stream);
            $resource = $metaData["uri"];
            $msg = "Cannot close " . $resource . ": $php_errormsg";
            throw new IOException($msg);
        }
        $this->stream = null;
    }

    /**
     * Flushes stream.
     *
     * @throws IOException if unable to flush data (e.g. stream is not open).
     */
    public function flush()
    {
        if (false === @fflush($this->stream)) {
            throw new IOException("Could not flush stream: " . $php_errormsg);
        }
    }

    /**
     * Writes data to stream.
     *
     * @param  string      $buf Binary/character data to write.
     * @param  int         $off (Optional) offset.
     * @param  int         $len (Optional) number of bytes/chars to write.
     * @return void
     * @throws IOException - if there is an error writing to stream
     */
    public function write($buf, $off = null, $len = null)
    {
        if ($off === null && $len === null) {
            $to_write = $buf;
        } elseif ($off !== null && $len === null) {
            $to_write = substr($buf, $off);
        } elseif ($off === null && $len !== null) {
            $to_write = substr($buf, 0, $len);
        } else {
            $to_write = substr($buf, $off, $len);
        }

        $result = @fwrite($this->stream, $to_write);

        if ($result === false) {
            throw new IOException("Error writing to stream.");
        }
    }

    /**
     * Returns a string representation of the attached PHP stream.
     * @return string
     */
    public function __toString()
    {
        return (string) $this->stream;
    }
}
<?php
/*
 *  $Id: 0c7982f0908210ce92301f7f79ed35f8ad7cdbe2 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/system/io/PhingFile.php';
require_once 'phing/system/io/Writer.php';

/**
 * Writer class for OutputStream objects.
 *
 * Unlike the Java counterpart, this class does not (yet) handle
 * character set transformations.  This will be an important function
 * of this class with move to supporting PHP6.
 *
 * @package   phing.system.io
 */
class OutputStreamWriter extends Writer
{

    /**
     * @var OutputStream
     */
    protected $outStream;

    /**
     * Construct a new OutputStreamWriter.
     * @param OutputStream $outStream OutputStream to write to
     */
    public function __construct(OutputStream $outStream)
    {
        $this->outStream = $outStream;
    }

    /**
     * Close the stream.
     * @return void
     */
    public function close()
    {
        $this->outStream->close();
    }

    /**
     * Write char data to stream.
     *
     * @param  string $buf
     * @param  int $off
     * @param  int $len
     *
     * @return void
     */
    public function write($buf, $off = null, $len = null)
    {
        return $this->outStream->write($buf, $off, $len);
    }

    /**
     * Flush output to the stream.
     */
    public function flush()
    {
        $this->outStream->flush();
    }

    /**
     * Gets a string representation of attached stream resource.
     *
     * @return string String representation of output stream
     */
    public function getResource()
    {
        return $this->outStream->__toString();
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/system/io/FileSystem.php';
include_once 'phing/system/lang/NullPointerException.php';

/**
 * An abstract representation of file and directory pathnames.
 *
 * @package   phing.system.io
 */
class PhingFile
{
    /** separator string, static, obtained from FileSystem */
    public static $separator;

    /** path separator string, static, obtained from FileSystem (; or :)*/
    public static $pathSeparator;

    /**
     * This abstract pathname's normalized pathname string.  A normalized
     * pathname string uses the default name-separator character and does not
     * contain any duplicate or redundant separators.
     */
    private $path = null;

    /**
     * The length of this abstract pathname's prefix, or zero if it has no prefix.
     * @var int
     */
    private $prefixLength = 0;

    /**
     * constructor
     *
     * @param mixed $arg1
     * @param mixed $arg2
     *
     * @throws IOException
     * @throws NullPointerException
     */
    public function __construct($arg1 = null, $arg2 = null)
    {

        if (self::$separator === null || self::$pathSeparator === null) {
            $fs = FileSystem::getFileSystem();
            self::$separator = $fs->getSeparator();
            self::$pathSeparator = $fs->getPathSeparator();
        }

        /* simulate signature identified constructors */
        if ($arg1 instanceof PhingFile && is_string($arg2)) {
            $this->_constructFileParentStringChild($arg1, $arg2);
        } elseif (is_string($arg1) && ($arg2 === null)) {
            $this->_constructPathname($arg1);
        } elseif (is_string($arg1) && is_string($arg2)) {
            $this->_constructStringParentStringChild($arg1, $arg2);
        } else {
            if ($arg1 === null) {
                throw new NullPointerException("Argument1 to function must not be null");
            }
            $this->path = (string) $arg1;
            $this->prefixLength = (int) $arg2;
        }
    }

    /**
     * Returns the length of this abstract pathname's prefix.
     *
     * @return int
     */
    public function getPrefixLength()
    {
        return (int) $this->prefixLength;
    }

    /* -- constructors not called by signature match, so we need some helpers --*/

    /**
     * @param string $pathname
     *
     * @throws NullPointerException
     */
    protected function _constructPathname($pathname)
    {
        // obtain ref to the filesystem layer
        $fs = FileSystem::getFileSystem();

        if ($pathname === null) {
            throw new NullPointerException("Argument to function must not be null");
        }

        $this->path = (string) $fs->normalize($pathname);
        $this->prefixLength = (int) $fs->prefixLength($this->path);
    }

    /**
     *
     * Enter description here ...
     * @param unknown_type $parent
     * @param unknown_type $child
     * @throws NullPointerException
     */
    protected function _constructStringParentStringChild($parent, $child = null)
    {
        // obtain ref to the filesystem layer
        $fs = FileSystem::getFileSystem();

        if ($child === null) {
            throw new NullPointerException("Argument to function must not be null");
        }
        if ($parent !== null) {
            if ($parent === "") {
                $this->path = $fs->resolve($fs->getDefaultParent(), $fs->normalize($child));
            } else {
                $this->path = $fs->resolve($fs->normalize($parent), $fs->normalize($child));
            }
        } else {
            $this->path = (string) $fs->normalize($child);
        }
        $this->prefixLength = (int) $fs->prefixLength($this->path);
    }

    /**
     *
     * Enter description here ...
     * @param unknown_type $parent
     * @param unknown_type $child
     * @throws NullPointerException
     */
    protected function _constructFileParentStringChild($parent, $child = null)
    {
        // obtain ref to the filesystem layer
        $fs = FileSystem::getFileSystem();

        if ($child === null) {
            throw new NullPointerException("Argument to function must not be null");
        }

        if ($parent !== null) {
            if ($parent->path === "") {
                $this->path = $fs->resolve($fs->getDefaultParent(), $fs->normalize($child));
            } else {
                $this->path = $fs->resolve($parent->path, $fs->normalize($child));
            }
        } else {
            $this->path = $fs->normalize($child);
        }
        $this->prefixLength = $fs->prefixLength($this->path);
    }

    /* -- Path-component accessors -- */

    /**
     * Returns the name of the file or directory denoted by this abstract
     * pathname.  This is just the last name in the pathname's name
     * sequence.  If the pathname's name sequence is empty, then the empty
     * string is returned.
     *
     * @return string The name of the file or directory denoted by this abstract
     *                pathname, or the empty string if this pathname's name sequence
     *                is empty
     */
    public function getName()
    {
        // that's a lastIndexOf
        $index = ((($res = strrpos($this->path, self::$separator)) === false) ? -1 : $res);
        if ($index < $this->prefixLength) {
            return substr($this->path, $this->prefixLength);
        }

        return substr($this->path, $index + 1);
    }

    /**
     * Returns the pathname string of this abstract pathname's parent, or
     * null if this pathname does not name a parent directory.
     *
     * The parent of an abstract pathname consists of the pathname's prefix,
     * if any, and each name in the pathname's name sequence except for the last.
     * If the name sequence is empty then the pathname does not name a parent
     * directory.
     *
     * @return string $pathname string of the parent directory named by this
     *                          abstract pathname, or null if this pathname does not name a parent
     */
    public function getParent()
    {
        // that's a lastIndexOf
        $index = ((($res = strrpos($this->path, self::$separator)) === false) ? -1 : $res);
        if ($index < $this->prefixLength) {
            if (($this->prefixLength > 0) && (strlen($this->path) > $this->prefixLength)) {
                return substr($this->path, 0, $this->prefixLength);
            }

            return null;
        }

        return substr($this->path, 0, $index);
    }

    /**
     * Returns the abstract pathname of this abstract pathname's parent,
     * or null if this pathname does not name a parent directory.
     *
     * The parent of an abstract pathname consists of the pathname's prefix,
     * if any, and each name in the pathname's name sequence except for the
     * last.  If the name sequence is empty then the pathname does not name
     * a parent directory.
     *
     * @return The abstract pathname of the parent directory named by this
     *             abstract pathname, or null if this pathname
     *             does not name a parent
     */
    public function getParentFile()
    {
        $p = $this->getParent();
        if ($p === null) {
            return null;
        }

        return new PhingFile((string) $p, (int) $this->prefixLength);
    }

    /**
     * Converts this abstract pathname into a pathname string.  The resulting
     * string uses the default name-separator character to separate the names
     * in the name sequence.
     *
     * @return string The string form of this abstract pathname
     */
    public function getPath()
    {
        return (string) $this->path;
    }

    /**
     * Returns path without leading basedir.
     *
     * @param string $basedir Base directory to strip
     *
     * @return string Path without basedir
     *
     * @uses getPath()
     */
    public function getPathWithoutBase($basedir)
    {
        if (!StringHelper::endsWith(self::$separator, $basedir)) {
            $basedir .= self::$separator;
        }
        $path = $this->getPath();
        if (substr($path, 0, strlen($basedir)) != $basedir) {
            //path does not begin with basedir, we don't modify it
            return $path;
        }

        return substr($path, strlen($basedir));
    }

    /**
     * Tests whether this abstract pathname is absolute.  The definition of
     * absolute pathname is system dependent.  On UNIX systems, a pathname is
     * absolute if its prefix is "/".  On Win32 systems, a pathname is absolute
     * if its prefix is a drive specifier followed by "\\", or if its prefix
     * is "\\".
     *
     * @return boolean true if this abstract pathname is absolute, false otherwise
     */
    public function isAbsolute()
    {
        return ($this->prefixLength !== 0);
    }

    /**
     * Returns the file extension for a given file. For example test.php would be returned as php.
     *
     * @return string The name of the extension.
     */
    public function getFileExtension()
    {
        return pathinfo((string) $this->getAbsolutePath(), PATHINFO_EXTENSION);
    }

    /**
     * Returns the absolute pathname string of this abstract pathname.
     *
     * If this abstract pathname is already absolute, then the pathname
     * string is simply returned as if by the getPath method.
     * If this abstract pathname is the empty abstract pathname then
     * the pathname string of the current user directory, which is named by the
     * system property user.dir, is returned.  Otherwise this
     * pathname is resolved in a system-dependent way.  On UNIX systems, a
     * relative pathname is made absolute by resolving it against the current
     * user directory.  On Win32 systems, a relative pathname is made absolute
     * by resolving it against the current directory of the drive named by the
     * pathname, if any; if not, it is resolved against the current user
     * directory.
     *
     * @return string The absolute pathname string denoting the same file or
     *                directory as this abstract pathname
     * @see    #isAbsolute()
     */
    public function getAbsolutePath()
    {
        $fs = FileSystem::getFileSystem();

        return $fs->resolveFile($this);
    }

    /**
     * Returns the absolute form of this abstract pathname.  Equivalent to
     * getAbsolutePath.
     *
     * @return PhingFile The absolute abstract pathname denoting the same file or
     *                directory as this abstract pathname
     */
    public function getAbsoluteFile()
    {
        return new PhingFile((string) $this->getAbsolutePath());
    }


    /**
     * Returns the canonical pathname string of this abstract pathname.
     *
     * A canonical pathname is both absolute and unique. The precise
     * definition of canonical form is system-dependent. This method first
     * converts this pathname to absolute form if necessary, as if by invoking the
     * getAbsolutePath() method, and then maps it to its unique form in a
     * system-dependent way.  This typically involves removing redundant names
     * such as "." and .. from the pathname, resolving symbolic links
     * (on UNIX platforms), and converting drive letters to a standard case
     * (on Win32 platforms).
     *
     * Every pathname that denotes an existing file or directory has a
     * unique canonical form.  Every pathname that denotes a nonexistent file
     * or directory also has a unique canonical form.  The canonical form of
     * the pathname of a nonexistent file or directory may be different from
     * the canonical form of the same pathname after the file or directory is
     * created.  Similarly, the canonical form of the pathname of an existing
     * file or directory may be different from the canonical form of the same
     * pathname after the file or directory is deleted.
     *
     * @return string The canonical pathname string denoting the same file or
     *                directory as this abstract pathname
     */
    public function getCanonicalPath()
    {
        $fs = FileSystem::getFileSystem();

        return $fs->canonicalize($this->path);
    }


    /**
     * Returns the canonical form of this abstract pathname.  Equivalent to
     * getCanonicalPath(.
     *
     * @return PhingFile The canonical pathname string denoting the same file or
     *                   directory as this abstract pathname
     */
    public function getCanonicalFile()
    {
        return new PhingFile($this->getCanonicalPath());
    }

    /**
     * Converts this abstract pathname into a file: URL.  The
     * exact form of the URL is system-dependent.  If it can be determined that
     * the file denoted by this abstract pathname is a directory, then the
     * resulting URL will end with a slash.
     *
     * Usage note: This method does not automatically escape
     * characters that are illegal in URLs.  It is recommended that new code
     * convert an abstract pathname into a URL by first converting it into a
     * URI, via the toURI() method, and then converting the URI
     * into a URL via the URI::toURL()
     *
     * @return void A URL object representing the equivalent file URL
     * @todo   Not implemented yet
     *
     */
    public function toURL()
    {
        /*
        // URL class not implemented yet
        return new URL("file", "", $this->_slashify($this->getAbsolutePath(), $this->isDirectory()));
        */
    }

    /**
     * Constructs a file: URI that represents this abstract pathname.
     * @todo   Not implemented yet
     * @return void
     */
    public function toURI()
    {
        /*
        $f = $this->getAbsoluteFile();
           $sp = (string) $this->slashify($f->getPath(), $f->isDirectory());
           if (StringHelper::startsWith('//', $sp))
        $sp = '//' + sp;

           return new URI('file', null, $sp, null);
        */
    }

    /**
     *
     * Enter description here ...
     * @param  PhingFile|string $path
     * @param  boolean          $isDirectory
     * @return string
     */
    public function _slashify($path, $isDirectory)
    {
        $p = (string) $path;

        if (self::$separator !== '/') {
            $p = str_replace(self::$separator, '/', $p);
        }

        if (!StringHelper::startsWith('/', $p)) {
            $p = '/' . $p;
        }

        if (!StringHelper::endsWith('/', $p) && $isDirectory) {
            $p = $p . '/';
        }

        return $p;
    }

    /* -- Attribute accessors -- */

    /**
     * Tests whether the application can read the file denoted by this
     * abstract pathname.
     *
     * @return boolean true if and only if the file specified by this
     *                 abstract pathname exists and can be read by the
     *                 application; false otherwise
     */
    public function canRead()
    {
        $fs = FileSystem::getFileSystem();

        if ($fs->checkAccess($this)) {
            return (boolean) @is_link($this->getAbsolutePath()) || @is_readable($this->getAbsolutePath());
        }

        return false;
    }

    /**
     * Tests whether the application can modify to the file denoted by this
     * abstract pathname.
     *
     * @return boolean true if and only if the file system actually
     *                 contains a file denoted by this abstract pathname and
     *                 the application is allowed to write to the file;
     *                 false otherwise.
     */
    public function canWrite()
    {
        $fs = FileSystem::getFileSystem();

        return $fs->checkAccess($this, true);
    }

    /**
     * Tests whether the file denoted by this abstract pathname exists.
     *
     * @return boolean true if and only if the file denoted by this
     *                 abstract pathname exists; false otherwise
     */
    public function exists()
    {
        clearstatcache();

        if (is_link($this->path)) {
            return true;
        } else {
            if ($this->isDirectory()) {
                return true;
            } else {
                return @file_exists($this->path) || is_link($this->path);
            }
        }
    }

    /**
     * Tests whether the file denoted by this abstract pathname is a
     * directory.
     *
     * @throws IOException
     * @return boolean true if and only if the file denoted by this
     *                 abstract pathname exists and is a directory;
     *                 false otherwise
     */
    public function isDirectory()
    {
        clearstatcache();
        $fs = FileSystem::getFileSystem();
        if ($fs->checkAccess($this) !== true) {
            throw new IOException("No read access to " . $this->path);
        }

        return @is_dir($this->path) && !@is_link($this->path);
    }

    /**
     * Tests whether the file denoted by this abstract pathname is a normal
     * file.  A file is normal if it is not a directory and, in
     * addition, satisfies other system-dependent criteria.  Any non-directory
     * file created by a Java application is guaranteed to be a normal file.
     *
     * @return boolean true if and only if the file denoted by this
     *                 abstract pathname exists and is a normal file;
     *                 false otherwise
     */
    public function isFile()
    {
        clearstatcache();
        //$fs = FileSystem::getFileSystem();
        return @is_file($this->path);
    }

    /**
     * Tests whether the file named by this abstract pathname is a hidden
     * file.  The exact definition of hidden is system-dependent.  On
     * UNIX systems, a file is considered to be hidden if its name begins with
     * a period character ('.').  On Win32 systems, a file is considered to be
     * hidden if it has been marked as such in the filesystem. Currently there
     * seems to be no way to dermine isHidden on Win file systems via PHP
     *
     * @throws IOException
     * @return boolean true if and only if the file denoted by this
     *                 abstract pathname is hidden according to the conventions of the
     *                 underlying platform
     */
    public function isHidden()
    {
        $fs = FileSystem::getFileSystem();
        if ($fs->checkAccess($this) !== true) {
            throw new IOException("No read access to " . $this->path);
        }

        return (($fs->getBooleanAttributes($this) & FileSystem::BA_HIDDEN) !== 0);
    }

    /**
     * Tests whether the file denoted by this abstract pathname is a symbolic link.
     *
     * @throws IOException
     * @return boolean true if and only if the file denoted by this
     *                 abstract pathname exists and is a symbolic link;
     *                 false otherwise
     */
    public function isLink()
    {
        clearstatcache();
        $fs = FileSystem::getFileSystem();
        if ($fs->checkAccess($this) !== true) {
            throw new IOException("No read access to " . $this->path);
        }

        return @is_link($this->path);
    }

    /**
     * Returns the target of the symbolic link denoted by this abstract pathname
     *
     * @return string the target of the symbolic link denoted by this abstract pathname
     */
    public function getLinkTarget()
    {
        return @readlink($this->path);
    }

    /**
     * Returns the time that the file denoted by this abstract pathname was
     * last modified.
     *
     * @throws IOException
     * @return int An integer value representing the time the file was
     *             last modified, measured in seconds since the epoch
     *             (00:00:00 GMT, January 1, 1970), or 0 if the
     *             file does not exist or if an I/O error occurs
     */
    public function lastModified()
    {
        $fs = FileSystem::getFileSystem();
        if ($fs->checkAccess($this) !== true) {
            throw new IOException("No read access to " . $this->path);
        }

        return $fs->getLastModifiedTime($this);
    }

    /**
     * Returns the length of the file denoted by this abstract pathname.
     * The return value is unspecified if this pathname denotes a directory.
     *
     * @throws IOException
     * @return int The length, in bytes, of the file denoted by this abstract
     *             pathname, or 0 if the file does not exist
     */
    public function length()
    {
        $fs = FileSystem::getFileSystem();
        if ($fs->checkAccess($this) !== true) {
            throw new IOException("No read access to " . $this->path . "\n");
        }

        return $fs->getLength($this);
    }

    /**
     * Convenience method for returning the contents of this file as a string.
     * This method uses file_get_contents() to read file in an optimized way.
     * @return string
     * @throws Exception - if file cannot be read
     */
    public function contents()
    {
        if (!$this->canRead() || !$this->isFile()) {
            throw new IOException("Cannot read file contents!");
        }

        return file_get_contents($this->getAbsolutePath());
    }

    /* -- File operations -- */

    /**
     * Atomically creates a new, empty file named by this abstract pathname if
     * and only if a file with this name does not yet exist.  The check for the
     * existence of the file and the creation of the file if it does not exist
     * are a single operation that is atomic with respect to all other
     * filesystem activities that might affect the file.
     *
     * @param bool $parents
     * @param int $mode
     * @throws IOException
     * @return boolean     true if the named file does not exist and was
     *                     successfully created; <code>false</code> if the named file
     *                     already exists
     */
    public function createNewFile($parents = true, $mode = 0777)
    {
        /** @var PhingFile $parent */
        $parent = $this->getParentFile();
        if ($parents && !$parent->exists()) {
            $parent->mkdirs();
        }
        $file = FileSystem::getFileSystem()->createNewFile($this->path);

        return $file;
    }

    /**
     * Deletes the file or directory denoted by this abstract pathname.  If
     * this pathname denotes a directory, then the directory must be empty in
     * order to be deleted.
     *
     * @param bool $recursive
     * @throws IOException
     * @return boolean true if and only if the file or directory is
     *                 successfully deleted; false otherwise
     */
    public function delete($recursive = false)
    {
        $fs = FileSystem::getFileSystem();
        if ($fs->canDelete($this) !== true) {
            throw new IOException("Cannot delete " . $this->path . "\n");
        }

        return $fs->delete($this, $recursive);
    }

    /**
     * Requests that the file or directory denoted by this abstract pathname
     * be deleted when php terminates.  Deletion will be attempted only for
     * normal termination of php and if and if only Phing::shutdown() is
     * called.
     *
     * Once deletion has been requested, it is not possible to cancel the
     * request.  This method should therefore be used with care.
     *
     */
    public function deleteOnExit()
    {
        $fs = FileSystem::getFileSystem();
        $fs->deleteOnExit($this);
    }

    /**
     * Returns an array of strings naming the files and directories in the
     * directory denoted by this abstract pathname.
     *
     * If this abstract pathname does not denote a directory, then this
     * method returns null  Otherwise an array of strings is
     * returned, one for each file or directory in the directory.  Names
     * denoting the directory itself and the directory's parent directory are
     * not included in the result.  Each string is a file name rather than a
     * complete path.
     *
     * There is no guarantee that the name strings in the resulting array
     * will appear in any specific order; they are not, in particular,
     * guaranteed to appear in alphabetical order.
     *
     * @param string $filter
     * @return array An array of strings naming the files and directories in the
     *               directory denoted by this abstract pathname.  The array will be
     *               empty if the directory is empty.  Returns null if
     *               this abstract pathname does not denote a directory, or if an
     *               I/O error occurs.
     */
    public function listDir($filter = null)
    {
        $fs = FileSystem::getFileSystem();

        return $fs->lister($this, $filter);
    }

    /**
     * @param string $filter
     *
     * @return array
     */
    public function listFiles($filter = null)
    {
        $ss = $this->listDir($filter);
        if ($ss === null) {
            return null;
        }
        $n = count($ss);
        $fs = array();
        for ($i = 0; $i < $n; $i++) {
            $fs[$i] = new PhingFile((string) $this->path, (string) $ss[$i]);
        }

        return $fs;
    }

    /**
     * Creates the directory named by this abstract pathname, including any
     * necessary but nonexistent parent directories.  Note that if this
     * operation fails it may have succeeded in creating some of the necessary
     * parent directories.
     *
     * @param int $mode
     * @throws IOException
     * @return boolean     true if and only if the directory was created,
     *                     along with all necessary parent directories; false
     *                     otherwise
     */
    public function mkdirs($mode = 0755)
    {
        if ($this->exists()) {
            return false;
        }
        try {
            if ($this->mkdir($mode)) {
                return true;
            }
        } catch (IOException $ioe) {
            // IOException from mkdir() means that directory propbably didn't exist.
        }
        $parentFile = $this->getParentFile();

        return (($parentFile !== null) && ($parentFile->mkdirs($mode) && $this->mkdir($mode)));
    }

    /**
     * Creates the directory named by this abstract pathname.
     *
     * @param int $mode
     * @throws IOException
     * @return boolean     true if and only if the directory was created; false otherwise
     */
    public function mkdir($mode = 0755)
    {
        $fs = FileSystem::getFileSystem();

        if ($fs->checkAccess(new PhingFile($this->path), true) !== true) {
            throw new IOException("No write access to " . $this->getPath());
        }

        return $fs->createDirectory($this, $mode);
    }

    /**
     * Renames the file denoted by this abstract pathname.
     *
     * @param  PhingFile $destFile The new abstract pathname for the named file
     * @throws IOException
     * @return boolean   true if and only if the renaming succeeded; false otherwise
     */
    public function renameTo(PhingFile $destFile)
    {
        $fs = FileSystem::getFileSystem();
        if ($fs->checkAccess($this) !== true) {
            throw new IOException("No write access to " . $this->getPath());
        }

        return $fs->rename($this, $destFile);
    }

    /**
     * Simple-copies file denoted by this abstract pathname into another
     * PhingFile
     *
     * @param  PhingFile $destFile The new abstract pathname for the named file
     * @throws IOException
     * @return boolean   true if and only if the renaming succeeded; false otherwise
     */
    public function copyTo(PhingFile $destFile)
    {
        $fs = FileSystem::getFileSystem();

        if ($fs->checkAccess($this) !== true) {
            throw new IOException("No read access to " . $this->getPath() . "\n");
        }

        if ($fs->checkAccess($destFile, true) !== true) {
            throw new IOException("File::copyTo() No write access to " . $destFile->getPath());
        }

        return $fs->copy($this, $destFile);
    }

    /**
     * Sets the last-modified time of the file or directory named by this
     * abstract pathname.
     *
     * All platforms support file-modification times to the nearest second,
     * but some provide more precision.  The argument will be truncated to fit
     * the supported precision.  If the operation succeeds and no intervening
     * operations on the file take place, then the next invocation of the
     * lastModified method will return the (possibly truncated) time argument
     * that was passed to this method.
     *
     * @param  int $time The new last-modified time, measured in milliseconds since
     *                       the epoch (00:00:00 GMT, January 1, 1970)
     * @throws Exception
     * @return boolean true if and only if the operation succeeded; false otherwise
     */
    public function setLastModified($time)
    {
        $time = (int) $time;
        if ($time < 0) {
            throw new Exception("IllegalArgumentException, Negative $time\n");
        }

        $fs = FileSystem::getFileSystem();

        return $fs->setLastModifiedTime($this, $time);
    }

    /**
     * Marks the file or directory named by this abstract pathname so that
     * only read operations are allowed.  After invoking this method the file
     * or directory is guaranteed not to change until it is either deleted or
     * marked to allow write access.  Whether or not a read-only file or
     * directory may be deleted depends upon the underlying system.
     *
     * @throws IOException
     * @return boolean true if and only if the operation succeeded; false otherwise
     */
    public function setReadOnly()
    {
        $fs = FileSystem::getFileSystem();
        if ($fs->checkAccess($this, true) !== true) {
            // Error, no write access
            throw new IOException("No write access to " . $this->getPath());
        }

        return $fs->setReadOnly($this);
    }

    /**
     * Sets the owner of the file.
     *
     * @param mixed $user User name or number.
     *
     * @throws IOException
     */
    public function setUser($user)
    {
        $fs = FileSystem::getFileSystem();

        return $fs->chown($this->getPath(), $user);
    }

    /**
     * Retrieve the owner of this file.
     *
     * @return int User ID of the owner of this file.
     */
    public function getUser()
    {
        return @fileowner($this->getPath());
    }

    /**
     * Sets the group of the file.
     *
     * @param string $group
     *
     * @throws IOException
     */
    public function setGroup($group)
    {
        $fs = FileSystem::getFileSystem();

        return $fs->chgrp($this->getPath(), $group);
    }

    /**
     * Retrieve the group of this file.
     *
     * @return int User ID of the owner of this file.
     */
    public function getGroup()
    {
        return @filegroup($this->getPath());
    }

    /**
     * Sets the mode of the file
     *
     * @param int $mode Ocatal mode.
     */
    public function setMode($mode)
    {
        $fs = FileSystem::getFileSystem();

        return $fs->chmod($this->getPath(), $mode);
    }

    /**
     * Retrieve the mode of this file.
     *
     * @return int
     */
    public function getMode()
    {
        return @fileperms($this->getPath());
    }

    /* -- Filesystem interface -- */

    /**
     * List the available filesystem roots.
     *
     * A particular platform may support zero or more hierarchically-organized
     * file systems.  Each file system has a root  directory from which all
     * other files in that file system can be reached.
     * Windows platforms, for example, have a root directory for each active
     * drive; UNIX platforms have a single root directory, namely "/".
     * The set of available filesystem roots is affected by various system-level
     * operations such the insertion or ejection of removable media and the
     * disconnecting or unmounting of physical or virtual disk drives.
     *
     * This method returns an array of PhingFile objects that
     * denote the root directories of the available filesystem roots.  It is
     * guaranteed that the canonical pathname of any file physically present on
     * the local machine will begin with one of the roots returned by this
     * method.
     *
     * The canonical pathname of a file that resides on some other machine
     * and is accessed via a remote-filesystem protocol such as SMB or NFS may
     * or may not begin with one of the roots returned by this method.  If the
     * pathname of a remote file is syntactically indistinguishable from the
     * pathname of a local file then it will begin with one of the roots
     * returned by this method.  Thus, for example, PhingFile objects
     * denoting the root directories of the mapped network drives of a Windows
     * platform will be returned by this method, while PhingFile
     * objects containing UNC pathnames will not be returned by this method.
     *
     * @return array An array of PhingFile objects denoting the available
     *               filesystem roots, or null if the set of roots
     *               could not be determined.  The array will be empty if there are
     *               no filesystem roots.
     */
    public function listRoots()
    {
        $fs = FileSystem::getFileSystem();

        return (array) $fs->listRoots();
    }

    /* -- Tempfile management -- */

    /**
     * Returns the path to the temp directory.
     * @return string
     */
    public static function getTempDir()
    {
        return Phing::getProperty('php.tmpdir');
    }

    /**
     * Static method that creates a unique filename whose name begins with
     * $prefix and ends with $suffix in the directory $directory. $directory
     * is a reference to a PhingFile Object.
     * Then, the file is locked for exclusive reading/writing.
     *
     * @author manuel holtgrewe, grin@gmx.net
     *
     * @param $prefix
     * @param $suffix
     * @param PhingFile $directory
     *
     * @throws IOException
     * @return PhingFile
     */
    public static function createTempFile($prefix, $suffix, PhingFile $directory)
    {

        // quick but efficient hack to create a unique filename ;-)
        $result = null;
        do {
            $result = new PhingFile($directory, $prefix . substr(md5(time()), 0, 8) . $suffix);
        } while (file_exists($result->getPath()));

        $fs = FileSystem::getFileSystem();
        $fs->createNewFile($result->getPath());
        $fs->lock($result);

        return $result;
    }

    /**
     * If necessary, $File the lock on $File is removed and then the file is
     * deleted.
     */
    public function removeTempFile()
    {
        $fs = FileSystem::getFileSystem();
        // catch IO Exception
        $fs->unlock($this);
        $this->delete();
    }

    /* -- Basic infrastructure -- */

    /**
     * Compares two abstract pathnames lexicographically.  The ordering
     * defined by this method depends upon the underlying system.  On UNIX
     * systems, alphabetic case is significant in comparing pathnames; on Win32
     * systems it is not.
     *
     * @param PhingFile $file Th file whose pathname sould be compared to the pathname of this file.
     *
     * @return int Zero if the argument is equal to this abstract pathname, a
     *             value less than zero if this abstract pathname is
     *             lexicographically less than the argument, or a value greater
     *             than zero if this abstract pathname is lexicographically
     *             greater than the argument
     */
    public function compareTo(PhingFile $file)
    {
        $fs = FileSystem::getFileSystem();

        return $fs->compare($this, $file);
    }

    /**
     * Tests this abstract pathname for equality with the given object.
     * Returns <code>true</code> if and only if the argument is not
     * <code>null</code> and is an abstract pathname that denotes the same file
     * or directory as this abstract pathname.  Whether or not two abstract
     * pathnames are equal depends upon the underlying system.  On UNIX
     * systems, alphabetic case is significant in comparing pathnames; on Win32
     * systems it is not.
     *
     * @param PhingFile $obj
     *
     * @return boolean
     */
    public function equals($obj)
    {
        if (($obj !== null) && ($obj instanceof PhingFile)) {
            return ($this->compareTo($obj) === 0);
        }

        return false;
    }

    /**
     * Backwards compatibility - @see __toString()
     *
     * @return string
     */
    public function toString()
    {
        return $this->__toString();
    }

    /**
     * Return string representation of the object
     *
     * @return string
     */
    public function __toString()
    {
        return $this->getPath();
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/system/io/BufferedWriter.php';
include_once 'phing/system/io/OutputStreamWriter.php';

class PrintStream
{
    /**
     * @var bool
     */
    private $autoFlush = false;

    /**
     * @var BufferedWriter
     */
    private $textOut;

    /**
     * @var OutputStream
     */
    protected $out;

    /**
     * @param OutputStream $out
     * @param bool $autoFlush
     */
    public function __construct(OutputStream $out, $autoFlush = false)
    {
        $this->out = $out;
        $this->autoFlush = $autoFlush;

        $this->textOut = new BufferedWriter(new OutputStreamWriter($out));
    }

    /**
     * @param mixed $value
     */
    public function println($value)
    {
        $this->prints($value);
        $this->newLine();
    }

    /**
     * @param mixed $value
     */
    public function prints($value)
    {
        if (is_bool($value)) {
            $value = $value === true ? 'true' : 'false';
        }

        $this->write((string) $value);
    }

    /**
     *
     */
    private function newLine()
    {
        $this->textOut->newLine();

        if ($this->autoFlush) {
            $this->textOut->flush();
        }
    }

    /**
     * @param string $buf
     * @param int $off
     * @param int $len
     */
    private function write($buf, $off = null, $len = null)
    {
        $this->textOut->write($buf, $off, $len);

        if ($this->autoFlush || $buff = '\n' && $this->autoFlush) {
            $this->textOut->flush();
        }
    }
}
<?php
/*
 *  $Id: 7acaed79c1abfad8021979dc7c81d086dfb58e3a $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

/**
 * Abstract class for reading character streams.
 *
 * @author Hans Lellelid <hans@xmpl.org>
 * @author Yannick Lecaillez <yl@seasonfive.com>
 * @version $Id: 7acaed79c1abfad8021979dc7c81d086dfb58e3a $
 * @package phing.system.io
 */
abstract class Reader
{

    /**
     * Read data from source.
     *
     * If length is specified, then only that number of chars is read,
     * otherwise stream is read until EOF.
     *
     * @param int $len
     */
    abstract public function read($len = null);

    /**
     * Close stream.
     * @throws IOException if there is an error closing stream
     */
    abstract public function close();

    /**
     * Returns the filename, url, etc. that is being read from.
     * This is critical for, e.g., ExpatParser's ability to know
     * the filename that is throwing an ExpatParserException, etc.
     * @return string
     */
    abstract public function getResource();

    /**
     * Move stream position relative to current pos.
     * @param int $n
     */
    public function skip($n)
    {
    }

    /**
     * Reset the current position in stream to beginning or last mark (if supported).
     */
    public function reset()
    {
    }

    /**
     * If supported, places a "marker" (like a bookmark) at current stream position.
     * A subsequent call to reset() will move stream position back
     * to last marker (if supported).
     */
    public function mark()
    {
    }

    /**
     * Whether marking is supported.
     * @return boolean
     */
    public function markSupported()
    {
        return false;
    }

    /**
     * Is stream ready for reading.
     * @return boolean
     */
    public function ready()
    {
        return true;
    }

}
<?php
/*
 *  $Id: a5eae277c8aacb2581042018690ebf58ff5e4a02 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Dummy class for reading from string of characters.
 * @package phing.system.io
 */
class StringReader extends Reader
{

    /**
     * @var string
     */
    private $_string;

    /**
     * @var int
     */
    private $mark = 0;

    /**
     * @var int
     */
    private $currPos = 0;

    /**
     * @param $string
     */
    public function __construct($string)
    {
        $this->_string = $string;
    }

    /**
     * @param int $n
     */
    public function skip($n)
    {
    }

    /**
     * @param null $len
     * @return int|string
     */
    public function read($len = null)
    {
        if ($len === null) {
            return $this->_string;
        } else {
            if ($this->currPos >= strlen($this->_string)) {
                return -1;
            }
            $out = substr($this->_string, $this->currPos, $len);
            $this->currPos += $len;

            return $out;
        }
    }

    public function mark()
    {
        $this->mark = $this->currPos;
    }

    public function reset()
    {
        $this->currPos = $this->mark;
    }

    public function close()
    {
    }

    public function open()
    {
    }

    public function ready()
    {
    }

    /**
     * @return bool
     */
    public function markSupported()
    {
        return true;
    }

    /**
     * @return string
     */
    public function getResource()
    {
        return '(string) "' . $this->_string . '"';
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/system/io/FileSystem.php';

/**
 * UnixFileSystem class. This class encapsulates the basic file system functions
 * for platforms using the unix (posix)-stylish filesystem. It wraps php native
 * functions suppressing normal PHP error reporting and instead uses Exception
 * to report and error.
 *
 * This class is part of a oop based filesystem abstraction and targeted to run
 * on all supported php platforms.
 *
 * Note: For debugging turn track_errors on in the php.ini. The error messages
 * and log messages from this class will then be clearer because $php_errormsg
 * is passed as part of the message.
 *
 * FIXME:
 *  - Comments
 *  - Error handling reduced to min, error are handled by PhingFile mainly
 *
 * @author    Andreas Aderhold, andi@binarycloud.com
 *
 * @package   phing.system.io
 */
class UnixFileSystem extends FileSystem
{
    /**
     * returns OS dependent path separator char
     *
     * @return string
     */
    public function getSeparator()
    {
        return '/';
    }

    /**
     * returns OS dependent directory separator char
     *
     * @return string
     */
    public function getPathSeparator()
    {
        return ':';
    }

    /**
     * A normal Unix pathname contains no duplicate slashes and does not end
     * with a slash.  It may be the empty string.
     *
     * Check that the given pathname is normal.  If not, invoke the real
     * normalizer on the part of the pathname that requires normalization.
     * This way we iterate through the whole pathname string only once.
     *
     * NOTE: this method no longer expands the tilde (~) character!
     *
     * @param string $strPathname
     *
     * @return string
     */
    public function normalize($strPathname)
    {
        if (!strlen($strPathname)) {
            return;
        }

        // Start normalising after any scheme that is present.
        // This prevents phar:///foo being normalised into phar:/foo
        // Use a regex as some paths may not by parsed by parse_url().
        if (preg_match('{^[a-z][a-z0-9+\-\.]+://}', $strPathname)) {
            $i = strpos($strPathname, '://') + 3;
        } else {
            $i = 0;
        }

        $n = strlen($strPathname);
        $prevChar = 0;
        for (; $i < $n; $i++) {
            $c = $strPathname{$i};
            if (($prevChar === '/') && ($c === '/')) {
                return self::normalizer($strPathname, $n, $i - 1);
            }
            $prevChar = $c;
        }
        if ($prevChar === '/') {
            return self::normalizer($strPathname, $n, $n - 1);
        }

        return $strPathname;
    }

    /**
     * Normalize the given pathname, whose length is $len, starting at the given
     * $offset; everything before this offset is already normal.
     *
     * @param string $pathname
     * @param int $len
     * @param int $offset
     *
     * @return string
     */
    protected function normalizer($pathname, $len, $offset)
    {
        if ($len === 0) {
            return $pathname;
        }
        $n = (int) $len;
        while (($n > 0) && ($pathname{$n - 1} === '/')) {
            $n--;
        }
        if ($n === 0) {
            return '/';
        }
        $sb = "";

        if ($offset > 0) {
            $sb .= substr($pathname, 0, $offset);
        }
        $prevChar = 0;
        for ($i = $offset; $i < $n; $i++) {
            $c = $pathname{$i};
            if (($prevChar === '/') && ($c === '/')) {
                continue;
            }
            $sb .= $c;
            $prevChar = $c;
        }

        return $sb;
    }

    /**
     * Compute the length of the pathname string's prefix.  The pathname
     * string must be in normal form.
     *
     * @param string $pathname
     *
     * @return int
     */
    public function prefixLength($pathname)
    {
        if (strlen($pathname) === 0) {
            return 0;
        }

        if (class_exists('Phar', false) && method_exists('Phar', 'running')) {
            $phar = Phar::running();
            $pharAlias = 'phar://' . Phing::PHAR_ALIAS;

            if ($phar && strpos($pathname, $phar) === 0) {
                return strlen($phar);
            }

            if ($phar && strpos($pathname, $pharAlias) === 0) {
                return strlen($pharAlias);
            }
        }

        return (($pathname{0} === '/') ? 1 : 0);
    }

    /**
     * Resolve the child pathname string against the parent.
     * Both strings must be in normal form, and the result
     * will be in normal form.
     *
     * @param string $parent
     * @param string $child
     *
     * @return string
     */
    public function resolve($parent, $child)
    {

        if ($child === "") {
            return $parent;
        }

        if ($child{0} === '/') {
            if ($parent === '/') {
                return $child;
            }

            return $parent . $child;
        }

        if ($parent === '/') {
            return $parent . $child;
        }

        return $parent . '/' . $child;
    }

    /**
     * @return string
     */
    public function getDefaultParent()
    {
        return '/';
    }

    /**
     * @param PhingFile $f
     *
     * @return bool
     */
    public function isAbsolute(PhingFile $f)
    {
        return ($f->getPrefixLength() !== 0);
    }

    /**
     * the file resolver
     *
     * @param PhingFile $f
     *
     * @return string
     */
    public function resolveFile(PhingFile $f)
    {
        // resolve if parent is a file oject only
        if ($this->isAbsolute($f)) {
            return $f->getPath();
        } else {
            return $this->resolve(Phing::getProperty("user.dir"), $f->getPath());
        }
    }

    /* -- most of the following is mapped to the php natives wrapped by FileSystem */

    /* -- Attribute accessors -- */
    /**
     * @param PhingFile $f
     * @return int
     */
    public function getBooleanAttributes($f)
    {
        //$rv = getBooleanAttributes0($f);
        $name = $f->getName();
        $hidden = (strlen($name) > 0) && ($name{0} == '.');

        return ($hidden ? FileSystem::BA_HIDDEN : 0);
    }

    /**
     * set file readonly on unix
     * @param PhingFile $f
     * @throws Exception
     * @throws IOException
     */
    public function setReadOnly($f)
    {
        if ($f instanceof PhingFile) {
            $strPath = (string) $f->getPath();
            $perms = (int) (@fileperms($strPath) & 0444);

            return FileSystem::getFileSystem()->chmod($strPath, $perms);
        } else {
            throw new Exception("IllegalArgumentType: Argument is not File");
        }
    }

    /**
     * compares file paths lexicographically
     * @param PhingFile $f1
     * @param PhingFile $f2
     * @return int|void
     */
    public function compare(PhingFile $f1, PhingFile $f2)
    {
        $f1Path = $f1->getPath();
        $f2Path = $f2->getPath();

        return strcmp((string) $f1Path, (string) $f2Path);
    }

    /**
     * Copy a file, takes care of symbolic links
     *
     * @param PhingFile $src  Source path and name file to copy.
     * @param PhingFile $dest Destination path and name of new file.
     *
     * @return void
     * @throws Exception if file cannot be copied.
     */
    public function copy(PhingFile $src, PhingFile $dest)
    {
        global $php_errormsg;

        if (!$src->isLink()) {
            return parent::copy($src, $dest);
        }

        $srcPath = $src->getAbsolutePath();
        $destPath = $dest->getAbsolutePath();

        $linkTarget = $src->getLinkTarget();
        if (false === @symlink($linkTarget, $destPath)) {
            $msg = "FileSystem::copy() FAILED. Cannot create symlink from $destPath to $linkTarget.";
            throw new Exception($msg);
        }
    }

    /* -- fs interface --*/

    /**
     * @return array
     */
    public function listRoots()
    {
        if (!$this->checkAccess('/', false)) {
            die ("Can not access root");
        }

        return array(new PhingFile("/"));
    }

    /**
     * returns the contents of a directory in an array
     * @param $f
     * @throws Exception
     * @return array
     */
    public function lister($f)
    {
        $dir = @opendir($f->getAbsolutePath());
        if (!$dir) {
            throw new Exception("Can't open directory " . $f->__toString());
        }
        $vv = array();
        while (($file = @readdir($dir)) !== false) {
            if ($file == "." || $file == "..") {
                continue;
            }
            $vv[] = (string) $file;
        }
        @closedir($dir);

        return $vv;
    }

    /**
     * @param string $p
     * @return string
     */
    public function fromURIPath($p)
    {
        if (StringHelper::endsWith("/", $p) && (strlen($p) > 1)) {

            // "/foo/" --> "/foo", but "/" --> "/"
            $p = substr($p, 0, strlen($p) - 1);

        }

        return $p;
    }

    /**
     * Whether file can be deleted.
     * @param  PhingFile $f
     * @return boolean
     */
    public function canDelete(PhingFile $f)
    {
        @clearstatcache();
        $dir = dirname($f->getAbsolutePath());

        return (bool) @is_writable($dir);
    }

}
<?php
/*
 *  $Id: 1ef78a7a6b9b116ebb4ec53c39f716f3ce1877e7 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/system/io/FileSystem.php';

/**
 * @package   phing.system.io
 */
class Win32FileSystem extends FileSystem
{

    protected $slash;
    protected $altSlash;
    protected $semicolon;

    private static $driveDirCache = array();

    /**
     *
     */
    public function __construct()
    {
        $this->slash = self::getSeparator();
        $this->semicolon = self::getPathSeparator();
        $this->altSlash = ($this->slash === '\\') ? '/' : '\\';
    }

    /**
     * @param $c
     * @return bool
     */
    public function isSlash($c)
    {
        return ($c == '\\') || ($c == '/');
    }

    /**
     * @param $c
     * @return bool
     */
    public function isLetter($c)
    {
        return ((ord($c) >= ord('a')) && (ord($c) <= ord('z')))
        || ((ord($c) >= ord('A')) && (ord($c) <= ord('Z')));
    }

    /**
     * @param $p
     * @return string
     */
    public function slashify($p)
    {
        if ((strlen($p) > 0) && ($p{0} != $this->slash)) {
            return $this->slash . $p;
        } else {
            return $p;
        }
    }

    /* -- Normalization and construction -- */

    /**
     * @return string
     */
    public function getSeparator()
    {
        // the ascii value of is the \
        return chr(92);
    }

    /**
     * @return string
     */
    public function getPathSeparator()
    {
        return ';';
    }

    /**
     * A normal Win32 pathname contains no duplicate slashes, except possibly
     * for a UNC prefix, and does not end with a slash.  It may be the empty
     * string.  Normalized Win32 pathnames have the convenient property that
     * the length of the prefix almost uniquely identifies the type of the path
     * and whether it is absolute or relative:
     *
     *    0  relative to both drive and directory
     *    1  drive-relative (begins with '\\')
     *    2  absolute UNC (if first char is '\\'), else directory-relative (has form "z:foo")
     *    3  absolute local pathname (begins with "z:\\")
     * @param $strPath
     * @param $len
     * @param $sb
     * @return int
     */
    public function normalizePrefix($strPath, $len, &$sb)
    {
        $src = 0;
        while (($src < $len) && $this->isSlash($strPath{$src})) {
            $src++;
        }
        $c = "";
        if (($len - $src >= 2)
            && $this->isLetter($c = $strPath{$src})
            && $strPath{$src + 1} === ':'
        ) {
            /* Remove leading slashes if followed by drive specifier.
             * This hack is necessary to support file URLs containing drive
             * specifiers (e.g., "file://c:/path").  As a side effect,
             * "/c:/path" can be used as an alternative to "c:/path". */
            $sb .= $c;
            $sb .= ':';
            $src += 2;
        } else {
            $src = 0;
            if (($len >= 2)
                && $this->isSlash($strPath{0})
                && $this->isSlash($strPath{1})
            ) {
                /* UNC pathname: Retain first slash; leave src pointed at
                 * second slash so that further slashes will be collapsed
                 * into the second slash.  The result will be a pathname
                 * beginning with "\\\\" followed (most likely) by a host
                 * name. */
                $src = 1;
                $sb .= $this->slash;
            }
        }

        return $src;
    }

    /** Normalize the given pathname, whose length is len, starting at the given
     * offset; everything before this offset is already normal.
     * @param $strPath
     * @param $len
     * @param $offset
     * @return string
     */
    protected function normalizer($strPath, $len, $offset)
    {
        if ($len == 0) {
            return $strPath;
        }
        if ($offset < 3) {
            $offset = 0; //Avoid fencepost cases with UNC pathnames
        }
        $src = 0;
        $slash = $this->slash;
        $sb = "";

        if ($offset == 0) {
            // Complete normalization, including prefix
            $src = $this->normalizePrefix($strPath, $len, $sb);
        } else {
            // Partial normalization
            $src = $offset;
            $sb .= substr($strPath, 0, $offset);
        }

        // Remove redundant slashes from the remainder of the path, forcing all
        // slashes into the preferred slash
        while ($src < $len) {
            $c = $strPath{$src++};
            if ($this->isSlash($c)) {
                while (($src < $len) && $this->isSlash($strPath{$src})) {
                    $src++;
                }
                if ($src === $len) {
                    /* Check for trailing separator */
                    $sn = (int) strlen($sb);
                    if (($sn == 2) && ($sb{1} === ':')) {
                        // "z:\\"
                        $sb .= $slash;
                        break;
                    }
                    if ($sn === 0) {
                        // "\\"
                        $sb .= $slash;
                        break;
                    }
                    if (($sn === 1) && ($this->isSlash($sb{0}))) {
                        /* "\\\\" is not collapsed to "\\" because "\\\\" marks
                        the beginning of a UNC pathname.  Even though it is
                        not, by itself, a valid UNC pathname, we leave it as
                        is in order to be consistent with the win32 APIs,
                        which treat this case as an invalid UNC pathname
                        rather than as an alias for the root directory of
                        the current drive. */
                        $sb .= $slash;
                        break;
                    }
                    // Path does not denote a root directory, so do not append
                    // trailing slash
                    break;
                } else {
                    $sb .= $slash;
                }
            } else {
                $sb .= $c;
            }
        }
        $rv = (string) $sb;

        return $rv;
    }

    /**
     * Check that the given pathname is normal.  If not, invoke the real
     * normalizer on the part of the pathname that requires normalization.
     * This way we iterate through the whole pathname string only once.
     * @param  string $strPath
     * @return string
     */
    public function normalize($strPath)
    {
        $strPath = $this->fixEncoding($strPath);

        if ($this->_isPharArchive($strPath)) {
            return str_replace('\\', '/', $strPath);
        }

        $n = strlen($strPath);
        $slash = $this->slash;
        $altSlash = $this->altSlash;
        $prev = 0;
        for ($i = 0; $i < $n; $i++) {
            $c = $strPath{$i};
            if ($c === $altSlash) {
                return $this->normalizer($strPath, $n, ($prev === $slash) ? $i - 1 : $i);
            }
            if (($c === $slash) && ($prev === $slash) && ($i > 1)) {
                return $this->normalizer($strPath, $n, $i - 1);
            }
            if (($c === ':') && ($i > 1)) {
                return $this->normalizer($strPath, $n, 0);
            }
            $prev = $c;
        }
        if ($prev === $slash) {
            return $this->normalizer($strPath, $n, $n - 1);
        }

        return $strPath;
    }

    /**
     * @param string $strPath
     * @return int
     */
    public function prefixLength($strPath)
    {
        if ($this->_isPharArchive($strPath)) {
            return 0;
        }

        $path = (string) $strPath;
        $slash = (string) $this->slash;
        $n = (int) strlen($path);
        if ($n === 0) {
            return 0;
        }
        $c0 = $path{0};
        $c1 = ($n > 1) ? $path{1} :
            0;
        if ($c0 === $slash) {
            if ($c1 === $slash) {
                return 2; // absolute UNC pathname "\\\\foo"
            }

            return 1; // drive-relative "\\foo"
        }

        if ($this->isLetter($c0) && ($c1 === ':')) {
            if (($n > 2) && ($path{2}) === $slash) {
                return 3; // Absolute local pathname "z:\\foo" */
            }

            return 2; // Directory-relative "z:foo"
        }

        return 0; // Completely relative
    }

    /**
     * @param string $parent
     * @param string $child
     * @return string
     */
    public function resolve($parent, $child)
    {
        $parent = (string) $parent;
        $child = (string) $child;
        $slash = (string) $this->slash;

        $pn = (int) strlen($parent);
        if ($pn === 0) {
            return $child;
        }
        $cn = (int) strlen($child);
        if ($cn === 0) {
            return $parent;
        }

        $c = $child;
        if (($cn > 1) && ($c{0} === $slash)) {
            if ($c{1} === $slash) {
                // drop prefix when child is a UNC pathname
                $c = substr($c, 2);
            } else {
                //Drop prefix when child is drive-relative */
                $c = substr($c, 1);
            }
        }

        $p = $parent;
        if ($p{$pn - 1} === $slash) {
            $p = substr($p, 0, $pn - 1);
        }

        return $p . $this->slashify($c);
    }

    /**
     * @return string
     */
    public function getDefaultParent()
    {
        return (string) ("" . $this->slash);
    }

    /**
     * @param string $strPath
     * @return string
     */
    public function fromURIPath($strPath)
    {
        $p = (string) $strPath;
        if ((strlen($p) > 2) && ($p{2} === ':')) {

            // "/c:/foo" --> "c:/foo"
            $p = substr($p, 1);

            // "c:/foo/" --> "c:/foo", but "c:/" --> "c:/"
            if ((strlen($p) > 3) && StringHelper::endsWith('/', $p)) {
                $p = substr($p, 0, strlen($p) - 1);
            }
        } elseif ((strlen($p) > 1) && StringHelper::endsWith('/', $p)) {
            // "/foo/" --> "/foo"
            $p = substr($p, 0, strlen($p) - 1);
        }

        return (string) $p;
    }

    /* -- Path operations -- */

    /**
     * @param PhingFile $f
     * @return bool
     */
    public function isAbsolute(PhingFile $f)
    {
        $pl = (int) $f->getPrefixLength();
        $p = (string) $f->getPath();

        return ((($pl === 2) && ($p{0} === $this->slash)) || ($pl === 3) || ($pl === 1 && $p{0} === $this->slash));
    }

    /** private
     * @param $d
     * @return int
     */
    public function _driveIndex($d)
    {
        $d = (string) $d{0};
        if ((ord($d) >= ord('a')) && (ord($d) <= ord('z'))) {
            return ord($d) - ord('a');
        }
        if ((ord($d) >= ord('A')) && (ord($d) <= ord('Z'))) {
            return ord($d) - ord('A');
        }

        return -1;
    }

    /** private
     * @param $strPath
     * @return bool
     */
    public function _isPharArchive($strPath)
    {
        return (strpos($strPath, 'phar://') === 0);
    }

    /**
     * @param $drive
     * @return null
     */
    public function _getDriveDirectory($drive)
    {
        $drive = (string) $drive{0};
        $i = (int) $this->_driveIndex($drive);
        if ($i < 0) {
            return null;
        }

        $s = (isset(self::$driveDirCache[$i]) ? self::$driveDirCache[$i] : null);

        if ($s !== null) {
            return $s;
        }

        $s = $this->_getDriveDirectory($i + 1);
        self::$driveDirCache[$i] = $s;

        return $s;
    }

    /**
     * @return string
     */
    public function _getUserPath()
    {
        //For both compatibility and security, we must look this up every time
        return (string) $this->normalize(Phing::getProperty("user.dir"));
    }

    /**
     * @param $path
     * @return null|string
     */
    public function _getDrive($path)
    {
        $path = (string) $path;
        $pl = $this->prefixLength($path);

        return ($pl === 3) ? substr($path, 0, 2) : null;
    }

    /**
     * @param PhingFile $f
     */
    public function resolveFile(PhingFile $f)
    {
        $path = $f->getPath();
        $pl = (int) $f->getPrefixLength();

        if (($pl === 2) && ($path{0} === $this->slash)) {
            return $path; // UNC
        }

        if ($pl === 3) {
            return $path; // Absolute local
        }

        if ($pl === 0) {
            if ($this->_isPharArchive($path)) {
                return $path;
            }

            return (string) ($this->_getUserPath() . $this->slashify($path)); //Completely relative
        }

        if ($pl === 1) { // Drive-relative
            $up = (string) $this->_getUserPath();
            $ud = (string) $this->_getDrive($up);
            if ($ud !== null) {
                return (string) $ud . $path;
            }

            return (string) $up . $path; //User dir is a UNC path
        }

        if ($pl === 2) { // Directory-relative
            $up = (string) $this->_getUserPath();
            $ud = (string) $this->_getDrive($up);
            if (($ud !== null) && StringHelper::startsWith($ud, $path)) {
                return (string) ($up . $this->slashify(substr($path, 2)));
            }
            $drive = (string) $path{0};
            $dir = (string) $this->_getDriveDirectory($drive);

            $np = (string) "";
            if ($dir !== null) {
                /* When resolving a directory-relative path that refers to a
                drive other than the current drive, insist that the caller
                have read permission on the result */
                $p = (string) $drive . (':' . $dir . $this->slashify(substr($path, 2)));

                if (!$this->checkAccess($p, false)) {
                    // FIXME
                    // throw security error
                    die("Can't resolve path $p");
                }

                return $p;
            }

            return (string) $drive . ':' . $this->slashify(substr($path, 2)); //fake it
        }

        throw new InvalidArgumentException("Unresolvable path: " . $path);
    }

    /* -- most of the following is mapped to the functions mapped th php natives in FileSystem */

    /* -- Attribute accessors -- */

    /**
     * @param PhingFile $f
     * @throws Exception
     */
    public function setReadOnly($f)
    {
        // dunno how to do this on win
        throw new Exception("WIN32FileSystem doesn't support read-only yet.");
    }

    /* -- Filesystem interface -- */

    /**
     * @param $path
     * @return bool
     * @throws Exception
     */
    protected function _access($path)
    {
        if (!$this->checkAccess($path, false)) {
            throw new Exception("Can't resolve path $p");
        }

        return true;
    }

    public function _nativeListRoots()
    {
        // FIXME
    }

    /**
     * @return array
     */
    public function listRoots()
    {
        $ds = $this->_nativeListRoots();
        $n = 0;
        for ($i = 0; $i < 26; $i++) {
            if ((($ds >> $i) & 1) !== 0) {
                if (!$this->_access((string) (chr(ord('A') + $i) . ':' . $this->slash))) {
                    $ds &= ~(1 << $i);
                } else {
                    $n++;
                }
            }
        }
        $fs = array();
        $j = (int) 0;

        for ($i = 0; $i < 26; $i++) {
            if ((($ds >> $i) & 1) !== 0) {
                $fs[$j++] = new PhingFile(chr(ord('A') + $i) . ':' . $this->slash);
            }
        }

        return $fs;
    }

    /* -- Basic infrastructure -- */

    /** compares file paths lexicographically
     * @param PhingFile $f1
     * @param PhingFile $f2
     * @return int
     */
    public function compare(PhingFile $f1, PhingFile $f2)
    {
        $f1Path = $f1->getPath();
        $f2Path = $f2->getPath();

        return strcasecmp((string) $f1Path, (string) $f2Path);
    }

    /**
     * returns the contents of a directory in an array
     * @param $f
     * @throws Exception
     * @return array
     */
    public function lister($f)
    {
        $dir = @opendir($f->getAbsolutePath());
        if (!$dir) {
            throw new Exception("Can't open directory " . $f->__toString());
        }
        $vv = array();
        while (($file = @readdir($dir)) !== false) {
            if ($file == "." || $file == "..") {
                continue;
            }
            $vv[] = (string) $file;
        }
        @closedir($dir);

        return $vv;
    }

    /**
     * On Windows platforms, PHP will mangle non-ASCII characters, see http://bugs.php.net/bug.php?id=47096
     *
     * @param $strPath
     * @return mixed|string
     */
    private function fixEncoding($strPath)
    {
        $codepage = 'CP' . trim(strstr(setlocale(LC_CTYPE, ''), '.'), '.');
        if (function_exists('iconv')) {
            $strPath = iconv('UTF-8', $codepage . '//IGNORE', $strPath);
        } elseif (function_exists('mb_convert_encoding')) {
            $strPath = mb_convert_encoding($strPath, $codepage, 'UTF-8');
        }
        return $strPath;
    }

}
<?php
/*
 *  $Id: 266f998d45e1dc5b48fb3c01dc7a5a596bc9d1b9 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/system/io/Win32FileSystem.php';

/**
 * FileSystem for Windows NT/2000.
 * @package phing.system.io
 */
class WinNTFileSystem extends Win32FileSystem
{

    /* -- class only for convenience and future use everything is inherinted --*/

}
<?php
/*
 *  $Id: 5b79c631c69a8d6481fc789cf847cd48a081b0e9 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Abstract class for writing character streams.
 *
 * @package   phing.system.io
 */
abstract class Writer
{

    /**
     * Writes data to output stream.
     * @param string $buf
     * @param int    $off
     * @param int    $len
     */
    abstract public function write($buf, $off = null, $len = null);

    /**
     * Close the stream.
     * @throws IOException - if there is an error closing stream.
     */
    abstract public function close();

    /**
     * Flush the stream, if supported by the stream.
     */
    public function flush()
    {
    }

    /**
     * Returns a string representation of resource filename, url, etc. that is being written to.
     * @return string
     */
    abstract public function getResource();
}
<?php
/*
 *  $Id: 6cbcbc795973d878e25774c8e813c159916e02c4 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */
include_once 'phing/system/io/FileParserInterface.php';

/**
 * Implements a YamlFileParser to parse yaml-files as array.
 *
 * @author Mike Lohmann <mike.lohmann@deck36.de>
 * @package phing.system.io
 */
class YamlFileParser implements FileParserInterface
{
    /**
     * {@inheritDoc}
     */
    public function parseFile(PhingFile $file)
    {
        if (!$file->canRead()) {
            throw new IOException("Unable to read file: " . $file);
        }

        try {
            
            if (!class_exists('\Symfony\Component\Yaml\Parser')) {
                throw new BuildException(
                    get_class($this)
                    . ' depends on \Symfony\Component\Yaml\Parser '
                    . 'being installed and on include_path.'
                );
            }
            // We load the Yaml class without the use of namespaces to prevent
            // parse errors in PHP 5.2.
            $parserClass = '\Symfony\Component\Yaml\Parser';
            $parser = new $parserClass;
            // Cast properties to array in case parse() returns null.
            $properties = (array) $parser->parse(file_get_contents($file->getAbsolutePath()));
        } catch (Exception $e) {
            if (is_a($e, '\Symfony\Component\Yaml\Exception\ParseException')) {
                throw new IOException("Unable to parse contents of " . $file . ": " . $e->getMessage());
            }
            throw $e;
        }

        $flattenedProperties = $this->flattenArray($properties);
        foreach ($flattenedProperties as $key => $flattenedProperty) {
            if (is_array($flattenedProperty)) {
                $flattenedProperties[$key] = implode(',', $flattenedProperty);
            }
        }

        return $flattenedProperties;
    }

    /**
     * Flattens an array to key => value.
     * @todo: milo - 20142901 - If you plan to extend phing and add a new fileparser, please move this to an abstract
     * class.
     *
     * @param array $arrayToFlatten
     */
    private function flattenArray(array $arrayToFlatten, $separator = '.', $flattenedKey = '')
    {
        $flattenedArray = array();
        foreach ($arrayToFlatten as $key => $value) {
            $tmpFlattendKey = (!empty($flattenedKey) ? $flattenedKey.$separator : '') . $key;
            // only append next value if is array and is an associative array
            if (is_array($value) && array_keys($value) !== range(0, count($value) - 1)) {
                $flattenedArray = array_merge(
                    $flattenedArray,
                    $this->flattenArray(
                        $value,
                        $separator,
                        $tmpFlattendKey
                    )
                );
            } else {
                $flattenedArray[$tmpFlattendKey] = $value;
            }
        }
        return $flattenedArray;
    }
}
<?php
/*
 *  $Id: cf13388f45a3ec1fb257a1cd14b68a6e97b099ad $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * @package phing.system.lang
 */
class Character
{

    // this class might be extended with plenty of ordinal char constants
    // and the like to support the multibyte aware datatype (char) in php
    // in form of an object.
    // anyway just a thought

    /**
     * @param $char
     * @return bool
     */
    public static function isLetter($char)
    {

        if (strlen($char) !== 1) {
            $char = 0;
        }

        $char = (int) ord($char);

        if ($char >= ord('A') && $char <= ord('Z')) {
            return true;
        }

        if ($char >= ord('a') && $char <= ord('z')) {
            return true;
        }

        return false;
    }

}
<?php
/*
 *  $Id: 28ebd7c756b8f49284fa9a4b29301a125e7e4844 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * @package phing.system.lang
 */
class EventObject
{

    /** The object on which the Event initially occurred. */
    protected $source;

    /** Constructs a prototypical Event.
     * @param $source
     * @throws Exception
     */
    public function __construct($source)
    {
        if ($source === null) {
            throw new Exception("Null source");
        }
        $this->source = $source;
    }

    /** The object on which the Event initially occurred. */
    public function getSource()
    {
        return $this->source;
    }

    /** Returns a String representation of this EventObject.*/
    public function toString()
    {
        if (method_exists($this->source, "toString")) {
            return get_class($this) . "[source=" . $this->source->toString() . "]";
        } else {
            return get_class($this) . "[source=" . get_class($this->source) . "]";
        }
    }
}
<?php
/*
 *  $Id: 203d7f55e3f1c0a9fc19a0603648ef688cfa68ae $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * @package   phing.system.lang
 */
class FileNotFoundException extends Exception
{
}
<?php
/*
 *  $Id: 8d0f260c2fee6d2107bfa59801719b70551dabd8 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * @package phing.system.lang
 */
class NullPointerException extends Exception
{
}
<?php
/*
 *  $Id: da792c6422c8e8b985c58d93efd578e45cbaeab4 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * @package phing.system.lang
 */
class SecurityException extends Exception
{
}
<?php

/*
 *  $Id: 190416eac21bc9e62ab01bec7b78357e18afa10a $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/system/io/PhingFile.php';
include_once 'phing/system/io/FileWriter.php';
include_once 'phing/system/io/FileParserInterface.php';
include_once 'phing/system/io/IniFileParser.php';

/**
 * Convenience class for reading and writing property files.
 *
 * FIXME
 *        - Add support for arrays (separated by ',')
 *
 * @package    phing.system.util
 * @version $Id: 190416eac21bc9e62ab01bec7b78357e18afa10a $
 */
class Properties
{

    private $properties = array();

    /**
     * @var FileParserInterface
     */
    private $fileParser;

    /**
     * @var PhingFile
     */
    private $file = null;

    /**
     * Constructor
     *
     * @param array $properties
     * @param FileParserInterface $fileParser
     */
    public function __construct($properties = null, FileParserInterface $fileParser = null)
    {
        $this->fileParser = $fileParser == null ? new IniFileParser() : $fileParser;

        if (is_array($properties)) {
            foreach ($properties as $key => $value) {
                $this->setProperty($key, $value);
            }
        }
    }

    /**
     * Load properties from a file.
     *
     * @param  PhingFile $file
     * @return void
     * @throws IOException - if unable to read file.
     */
    public function load(PhingFile $file)
    {
        if ($file->canRead()) {
            $this->parse($file, false);

            $this->file = $file;
        } else {
            throw new IOException("Can not read file " . $file->getPath());
        }
    }

    /**
     * Parses the file given.
     *
     * @param  PhingFile $file
     * @internal param bool $processSections Whether to honor [SectionName] sections in INI file.
     * @return array   Properties loaded from file (no prop replacements done yet).
     */
    protected function parse(PhingFile $file)
    {
        $this->properties = $this->fileParser->parseFile($file);
    }

    /**
     * Process values when being written out to properties file.
     * does things like convert true => "true"
     * @param  mixed  $val The property value (may be boolean, etc.)
     * @return string
     */
    protected function outVal($val)
    {
        if ($val === true) {
            $val = "true";
        } elseif ($val === false) {
            $val = "false";
        }

        return $val;
    }

    /**
     * Create string representation that can be written to file and would be loadable using load() method.
     *
     * Essentially this function creates a string representation of properties that is ready to
     * write back out to a properties file.  This is used by store() method.
     *
     * @return string
     */
    public function toString()
    {
        $buf = "";
        foreach ($this->properties as $key => $item) {
            $buf .= $key . "=" . $this->outVal($item) . PHP_EOL;
        }

        return $buf;
    }

    /**
     * Stores current properties to specified file.
     *
     * @param  PhingFile   $file   File to create/overwrite with properties.
     * @param  string      $header Header text that will be placed (within comments) at the top of properties file.
     * @return void
     * @throws IOException - on error writing properties file.
     */
    public function store(PhingFile $file = null, $header = null)
    {
        if ($file == null) {
            $file = $this->file;
        }

        if ($file == null) {
            throw new IOException("Unable to write to empty filename");
        }

        // stores the properties in this object in the file denoted
        // if file is not given and the properties were loaded from a
        // file prior, this method stores them in the file used by load()
        try {
            $fw = new FileWriter($file);
            if ($header !== null) {
                $fw->write("# " . $header . PHP_EOL);
            }
            $fw->write($this->toString());
            $fw->close();
        } catch (IOException $e) {
            throw new IOException("Error writing property file: " . $e->getMessage());
        }
    }

    public function storeOutputStream(OutputStream $os, $comments)
    {
        $this->_storeOutputStream(new BufferedWriter(new OutputStreamWriter($os)), $comments);
    }

    private function _storeOutputStream(BufferedWriter $bw, $comments)
    {
        if ($comments != null) {
            self::writeComments($bw, $comments);
        }
        $bw->write("#" . gmdate('D, d M Y H:i:s', time()) . ' GMT');
        $bw->newLine();
        foreach ($this->getProperties() as $key => $value) {
                $bw->write($key . "=" . $value);
                $bw->newLine();

        }
        $bw->flush();
    }

    private static function writeComments(BufferedWriter $bw, $comments)
    {
        $rows = explode("\n", $comments);
        $bw->write("#" . PHP_EOL);
        foreach ($rows as $row) {
            $bw->write(sprintf("#%s%s", trim($row), PHP_EOL));
        }
        $bw->write("#");
        $bw->newLine();
    }

    /**
     * Returns copy of internal properties hash.
     * Mostly for performance reasons, property hashes are often
     * preferable to passing around objects.
     *
     * @return array
     */
    public function getProperties()
    {
        return $this->properties;
    }

    /**
     * Get value for specified property.
     * This is the same as get() method.
     *
     * @param  string $prop The property name (key).
     * @return mixed
     * @see get()
     */
    public function getProperty($prop)
    {
        if (!isset($this->properties[$prop])) {
            return null;
        }

        return $this->properties[$prop];
    }

    /**
     * Get value for specified property.
     * This function exists to provide a hashtable-like interface for
     * properties.
     *
     * @param  string $prop The property name (key).
     * @return mixed
     * @see getProperty()
     */
    public function get($prop)
    {
        if (!isset($this->properties[$prop])) {
            return null;
        }

        return $this->properties[$prop];
    }

    /**
     * Set the value for a property.
     *
     * @param  string $key
     * @param  mixed  $value
     * @return mixed  Old property value or null if none was set.
     */
    public function setProperty($key, $value)
    {
        $oldValue = null;
        if (isset($this->properties[$key])) {
            $oldValue = $this->properties[$key];
        }
        $this->properties[$key] = $value;

        return $oldValue;
    }

    /**
     * Set the value for a property.
     * This function exists to provide hashtable-lie
     * interface for properties.
     *
     * @param string $key
     * @param mixed $value
     * @return mixed
     */
    public function put($key, $value)
    {
        return $this->setProperty($key, $value);
    }

    /**
     * Appends a value to a property if it already exists with a delimiter
     *
     * If the property does not, it just adds it.
     *
     * @param string $key
     * @param mixed  $value
     * @param string $delimiter
     */
    public function append($key, $value, $delimiter = ',')
    {
        $newValue = $value;
        if (isset($this->properties[$key]) && !empty($this->properties[$key])) {
            $newValue = $this->properties[$key] . $delimiter . $value;
        }
        $this->properties[$key] = $newValue;
    }

    /**
     * Same as keys() function, returns an array of property names.
     * @return array
     */
    public function propertyNames()
    {
        return $this->keys();
    }

    /**
     * Whether loaded properties array contains specified property name.
     * @param $key
     * @return boolean
     */
    public function containsKey($key)
    {
        return isset($this->properties[$key]);
    }

    /**
     * Returns properties keys.
     * Use this for foreach () {} iterations, as this is
     * faster than looping through property values.
     * @return array
     */
    public function keys()
    {
        return array_keys($this->properties);
    }

    /**
     * Whether properties list is empty.
     * @return boolean
     */
    public function isEmpty()
    {
        return empty($this->properties);
    }
}
<?php

/**
 * Static class to handle a slot-listening system.
 *
 * Unlike the slots/signals Qt model, this class manages something that is
 * more like a simple hashtable, where each slot has only one value.  For that
 * reason "Registers" makes more sense, the reference being to CPU registers.
 *
 * This could be used for anything, but it's been built for a pretty specific phing
 * need, and that is to allow access to dynamic values that are set by logic
 * that is not represented in a build file.  For exampe, we need a system for getting
 * the current resource (file) that is being processed by a filterchain in a fileset.
 *
 * Each slot corresponds to only one read-only, dynamic-value RegisterSlot object. In
 * a build.xml register slots are expressed using a syntax similar to variables:
 *
 * <replaceregexp>
 *    <regexp pattern="\n" replace="%{task.current_file}"/>
 * </replaceregexp>
 *
 * The task/type must provide a supporting setter for the attribute:
 *
 * <code>
 *     function setListeningReplace(RegisterSlot $slot) {
 *        $this->replace = $slot;
 *  }
 *
 *  // in main()
 *  if ($this->replace instanceof RegisterSlot) {
 *        $this->regexp->setReplace($this->replace->getValue());
 *  } else {
 *        $this->regexp->setReplace($this->replace);
 *  }
 * </code>
 *
 * @author Hans Lellelid <hans@xmpl.org>
 * @version $Id: 0685f6aeb304d78c4ccb4eb966bdfec14745d4a7 $
 * @package phing.system.util
 */
class Register
{

    /** Slots that have been registered */
    private static $slots = array();

    /**
     * Returns RegisterSlot for specified key.
     *
     * If not slot exists a new one is created for key.
     *
     * @param  string       $key
     * @return RegisterSlot
     */
    public static function getSlot($key)
    {
        if (!isset(self::$slots[$key])) {
            self::$slots[$key] = new RegisterSlot($key);
        }

        return self::$slots[$key];
    }
}

/**
 * Represents a slot in the register.
 *
 * @package phing.system.util
 */
class RegisterSlot
{

    /** The name of this slot. */
    private $key;

    /** The value for this slot. */
    private $value;

    /**
     * Constructs a new RegisterSlot, setting the key to passed param.
     * @param string $key
     */
    public function __construct($key)
    {
        $this->key = (string) $key;
    }

    /**
     * Sets the key / name for this slot.
     * @param string $k
     */
    public function setKey($k)
    {
        $this->key = (string) $k;
    }

    /**
     * Gets the key / name for this slot.
     * @return string
     */
    public function getKey()
    {
        return $this->key;
    }

    /**
     * Sets the value for this slot.
     * @param mixed
     */
    public function setValue($v)
    {
        $this->value = $v;
    }

    /**
     * Returns the value at this slot.
     * @return mixed
     */
    public function getValue()
    {
        return $this->value;
    }

    /**
     * Recursively implodes an array to a comma-separated string
     * @param  array  $arr
     * @return string
     */
    private function implodeArray(array $arr)
    {
        $values = array();

        foreach ($arr as $value) {
            if (is_array($value)) {
                $values[] = $this->implodeArray($value);
            } else {
                $values[] = $value;
            }
        }

        return "{" . implode(",", $values) . "}";
    }

    /**
     * Returns the value at this slot as a string value.
     * @return string
     */
    public function __toString()
    {
        if (is_array($this->value)) {
            return $this->implodeArray($this->value);
        } else {
            return (string) $this->value;
        }
    }

}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * This class can be used to obtain the execution time of all of the scripts
 * that are executed in the process of building a page.
 *
 * Example:
 * To be done before any scripts execute:
 *
 * $Timer = new Timer;
 * $Timer->Start_Timer();
 *
 * To be done after all scripts have executed:
 *
 * $timer->Stop_Timer();
 * $timer->Get_Elapsed_Time(int number_of_places);
 *
 * @author    Charles Killian
 * @author    Hans Lellelid <hans@xmpl.org>
 *
 * @package    phing.system.util
 */
class Timer
{
    /**
     * start time
     *
     * @var float
     */
    protected $stime;

    /**
     * end time
     *
     * @var float
     */
    protected $etime;

    /**
     * This function sets the class variable $stime to the current time in
     * microseconds.
     *
     * @return void
     */
    public function start()
    {
        $this->stime = microtime(true);
    }

    /**
     * This function sets the class variable $etime to the current time in
     * microseconds.
     *
     * @return void
     */
    public function stop()
    {
        $this->etime = microtime(true);
    }

    /**
     * This function returns the elapsed time in seconds.
     *
     * Call start_time() at the beginning of script execution and end_time() at
     * the end of script execution.  Then, call elapsed_time() to obtain the
     * difference between start_time() and end_time().
     *
     * @param  int $places decimal place precision of elapsed time (default is 5)
     *
     * @return string Properly formatted time.
     */
    public function getElapsedTime($places = 5)
    {
        $etime = $this->etime - $this->stime;
        $format = "%0." . $places . "f";

        return (sprintf($format, $etime));
    }
}
<?php
/*
 * $Id: fdff5ad4aee28d30831176c675a76fd0dc823658 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/TaskContainer.php';

/**
 * The Target component. Carries all required target data. Implements the
 * abstract class {@link TaskContainer}
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: fdff5ad4aee28d30831176c675a76fd0dc823658 $
 * @see       TaskContainer
 * @package   phing
 */
class Target implements TaskContainer
{

    /**
     * Name of target
     * @var string
     */
    private $name;

    /**
     * Dependencies
     * @var array
     */
    private $dependencies = array();

    /**
     * Holds objects of children of this target
     * @var array
     */
    private $children = array();

    /**
     * The if condition from xml
     * @var string
     */
    private $ifCondition = "";

    /**
     * The unless condition from xml
     * @var string
     */
    private $unlessCondition = "";

    /**
     * Description of this target
     * @var string
     */
    private $description;

    /**
     * Whether to hide target in targets list (-list -p switches)
     * @var boolean
     */
    private $hidden = false;

    /**
     * Whether to log message as INFO or VERBOSE if target skipped
     * @var boolean
     */
    private $logSkipped = false;

    /**
     * Rreference to project
     * @var Project
     */
    private $project;

    /**
     * References the project to the current component.
     *
     * @param Project $project The reference to the current project
     */
    public function setProject(Project $project)
    {
        $this->project = $project;
    }

    /**
     * Returns reference to current project
     *
     * @return Project Reference to current porject object
     */
    public function getProject()
    {
        return $this->project;
    }

    /**
     * Sets the target dependencies from xml
     *
     * @param  string         $depends Comma separated list of targetnames that depend on
     *                                 this target
     * @throws BuildException
     */
    public function setDepends($depends)
    {
        // explode should be faster than strtok
        $deps = explode(',', $depends);
        for ($i = 0, $size = count($deps); $i < $size; $i++) {
            $trimmed = trim($deps[$i]);
            if ($trimmed === "") {
                throw new BuildException("Syntax Error: Depend attribute for target " . $this->getName(
                    ) . " is malformed.");
            }
            $this->addDependency($trimmed);
        }
    }

    /**
     * Adds a singular dependent target name to the list
     *
     * @param string $dependency The dependency target to add
     */
    public function addDependency($dependency)
    {
        $this->dependencies[] = (string) $dependency;
    }

    /**
     * Returns reference to indexed array of the dependencies this target has.
     *
     * @return array Reference to target dependencoes
     */
    public function getDependencies()
    {
        return $this->dependencies;
    }

    /**
     * Sets the name of the target
     *
     * @param string $name Name of this target
     */
    public function setName($name)
    {
        $this->name = (string) $name;
    }

    /**
     * Returns name of this target.
     *
     * @return string The name of the target
     */
    public function getName()
    {
        return (string) $this->name;
    }

    /**
     * Set target status. If true, target does not come in phing -list
     *
     * @param  boolean $flag
     * @return Target
     */
    public function setHidden($flag)
    {
        $this->hidden = (boolean) $flag;

        return $this;
    }

    /**
     * Get target status. If true, target does not come in phing -list
     *
     * @return boolean
     */
    public function getHidden()
    {
        return $this->hidden;
    }

    /**
     * Alias for getHidden()
     *
     * @return boolean
     */
    public function isHidden()
    {
        return $this->getHidden();
    }

    /**
     * Adds a task element to the list of this targets child elements
     *
     * @param Task $task The task object to add
     */
    public function addTask(Task $task)
    {
        $this->children[] = $task;
    }

    /**
     * Adds a runtime configurable element to the list of this targets child
     * elements.
     *
     * @param RuntimeConfigurable $rtc The RuntimeConfigurable object
     */
    public function addDataType($rtc)
    {
        $this->children[] = $rtc;
    }

    /**
     * Returns an array of all tasks this target has as childrens.
     *
     * The task objects are copied here. Don't use this method to modify
     * task objects.
     *
     * @return array Task[]
     */
    public function getTasks()
    {
        $tasks = array();
        for ($i = 0, $size = count($this->children); $i < $size; $i++) {
            $tsk = $this->children[$i];
            if ($tsk instanceof Task) {
                // note: we're copying objects here!
                $tasks[] = clone $tsk;
            }
        }

        return $tasks;
    }

    /**
     * Set the if-condition from the XML tag, if any. The property name given
     * as parameter must be present so the if condition evaluates to true
     *
     * @param string $property The property name that has to be present
     */
    public function setIf($property)
    {
        $this->ifCondition = ($property === null) ? "" : $property;
    }

    /**
     * Set the unless-condition from the XML tag, if any. The property name
     * given as parameter must be present so the unless condition evaluates
     * to true
     *
     * @param string $property The property name that has to be present
     */
    public function setUnless($property)
    {
        $this->unlessCondition = ($property === null) ? "" : $property;
    }

    /**
     * Sets a textual description of this target.
     *
     * @param string $description The description text
     */
    public function setDescription($description)
    {
        if ($description !== null && strcmp($description, "") !== 0) {
            $this->description = (string) $description;
        } else {
            $this->description = null;
        }
    }

    /**
     * Returns the description of this target.
     *
     * @return string The description text of this target
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * @param $log
     */
    public function setLogSkipped($log)
    {
        $this->logSkipped = (bool) $log;
    }

    /**
     * @return bool|null
     */
    public function getLogSkipped()
    {
        if ($this->logSkipped === null) {
            $this->setLogSkipped(false);
        }

        return $this->logSkipped;
    }

    /**
     * Returns a string representation of this target. In our case it
     * simply returns the target name field
     *
     * @return string The string representation of this target
     */
    public function toString()
    {
        return (string) $this->name;
    }

    /**
     * The entry point for this class. Does some checking, then processes and
     * performs the tasks for this target.
     */
    public function main()
    {
        if ($this->testIfCondition() && $this->testUnlessCondition()) {
            foreach ($this->children as $o) {
                if ($o instanceof Task) {
                    // child is a task
                    $o->perform();
                } else {
                    // child is a RuntimeConfigurable
                    $o->maybeConfigure($this->project);
                }
            }
        } elseif (!$this->testIfCondition()) {
            $this->project->log(
                "Skipped target '" . $this->name . "' because property '" . $this->ifCondition . "' not set.",
                $this->getLogSkipped() ? Project::MSG_INFO : Project::MSG_VERBOSE
            );
        } else {
            $this->project->log(
                "Skipped target '" . $this->name . "' because property '" . $this->unlessCondition . "' set.",
                $this->getLogSkipped() ? Project::MSG_INFO : Project::MSG_VERBOSE
            );
        }
    }

    /**
     * Performs the tasks by calling the main method of this target that
     * actually executes the tasks.
     *
     * This method is for ZE2 and used for proper exception handling of
     * task exceptions.
     */
    public function performTasks()
    {
        try { // try to execute this target
            $this->project->fireTargetStarted($this);
            $this->main();
            $this->project->fireTargetFinished($this, $null = null);
        } catch (BuildException $exc) {
            // log here and rethrow
            $this->project->fireTargetFinished($this, $exc);
            throw $exc;
        }
    }

    /**
     * Tests if the property set in ifConfiditon exists.
     *
     * @return boolean <code>true</code> if the property specified
     *                 in <code>$this->ifCondition</code> exists;
     *                 <code>false</code> otherwise
     */
    private function testIfCondition()
    {
        if ($this->ifCondition === "") {
            return true;
        }

        $properties = explode(",", $this->ifCondition);

        $result = true;
        foreach ($properties as $property) {
            $test = ProjectConfigurator::replaceProperties(
                $this->getProject(),
                $property,
                $this->project->getProperties()
            );
            $result = $result && ($this->project->getProperty($test) !== null);
        }

        return $result;
    }

    /**
     * Tests if the property set in unlessCondition exists.
     *
     * @return boolean <code>true</code> if the property specified
     *                 in <code>$this->unlessCondition</code> exists;
     *                 <code>false</code> otherwise
     */
    private function testUnlessCondition()
    {
        if ($this->unlessCondition === "") {
            return true;
        }

        $properties = explode(",", $this->unlessCondition);

        $result = true;
        foreach ($properties as $property) {
            $test = ProjectConfigurator::replaceProperties(
                $this->getProject(),
                $property,
                $this->project->getProperties()
            );
            $result = $result && ($this->project->getProperty($test) === null);
        }

        return $result;
    }

}
<?php
/*
 *  $Id: b1b87e93a3e875cdbc3e27db9244118557e4e5d2 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/ProjectComponent.php';
include_once 'phing/RuntimeConfigurable.php';

/**
 * The base class for all Tasks.
 *
 * Use {@link Project#createTask} to register a new Task.
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: b1b87e93a3e875cdbc3e27db9244118557e4e5d2 $
 * @see       Project#createTask()
 * @package   phing
 */
abstract class Task extends ProjectComponent
{

    /**
     * Owning Target object
     * @var Target
     */
    protected $target;

    /**
     * Description of the task
     * @var string
     */
    protected $description;

    /**
     * Internal taskname (req)
     * @var string
     */
    protected $taskType;

    /**
     * Taskname for logger
     * @var string
     */
    protected $taskName;

    /**
     * Stored buildfile location
     * @var Location
     */
    protected $location;

    /**
     * Wrapper of the task
     * @var RuntimeConfigurable
     */
    protected $wrapper;

    /**
     * Sets the owning target this task belongs to.
     *
     * @param Target Reference to owning target
     */
    public function setOwningTarget(Target $target)
    {
        $this->target = $target;
    }

    /**
     * Returns the owning target of this task.
     *
     * @return Target The target object that owns this task
     */
    public function getOwningTarget()
    {
        return $this->target;
    }

    /**
     * Returns the name of task, used only for log messages
     *
     * @return string Name of this task
     */
    public function getTaskName()
    {
        if ($this->taskName === null) {
            // if no task name is set, then it's possible
            // this task was created from within another task.  We don't
            // therefore know the XML tag name for this task, so we'll just
            // use the class name stripped of "task" suffix.  This is only
            // for log messages, so we don't have to worry much about accuracy.
            return preg_replace('/task$/i', '', get_class($this));
        }

        return $this->taskName;
    }

    /**
     * Sets the name of this task for log messages
     *
     * @param  string $name
     * @return string A string representing the name of this task for log
     */
    public function setTaskName($name)
    {
        $this->taskName = (string) $name;
    }

    /**
     * Returns the name of the task under which it was invoked,
     * usually the XML tagname
     *
     * @return string The type of this task (XML Tag)
     */
    public function getTaskType()
    {
        return $this->taskType;
    }

    /**
     * Sets the type of the task. Usually this is the name of the XML tag
     *
     * @param string The type of this task (XML Tag)
     */
    public function setTaskType($name)
    {
        $this->taskType = (string) $name;
    }

    /**
     * Returns a name
     * @param string $slotName
     * @return \RegisterSlot
     */
    protected function getRegisterSlot($slotName)
    {
        return Register::getSlot('task.' . $this->getTaskName() . '.' . $slotName);
    }

    /**
     * Provides a project level log event to the task.
     *
     * @param string $msg The message to log
     * @param integer $level The priority of the message
     * @see BuildEvent
     * @see BuildListener
     */
    public function log($msg, $level = Project::MSG_INFO)
    {
        $this->project->logObject($this, $msg, $level);
    }

    /**
     * Sets a textual description of the task
     *
     * @param string $desc The text describing the task
     */
    public function setDescription($desc)
    {
        $this->description = $desc;
    }

    /**
     * Returns the textual description of the task
     *
     * @return string The text description of the task
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Called by the parser to let the task initialize properly.
     * Should throw a BuildException if something goes wrong with the build
     *
     * This is abstract here, but may not be overloaded by subclasses.
     *
     * @throws BuildException
     */
    public function init()
    {
    }

    /**
     *  Called by the project to let the task do it's work. This method may be
     *  called more than once, if the task is invoked more than once. For
     *  example, if target1 and target2 both depend on target3, then running
     *  <em>phing target1 target2</em> will run all tasks in target3 twice.
     *
     *  Should throw a BuildException if someting goes wrong with the build
     *
     *  This is abstract here. Must be overloaded by real tasks.
     */
    abstract public function main();

    /**
     * Returns the location within the buildfile this task occurs. Used
     * by {@link BuildException} to give detailed error messages.
     *
     * @return Location The location object describing the position of this
     *                  task within the buildfile.
     */
    public function getLocation()
    {
        return $this->location;
    }

    /**
     * Sets the location within the buildfile this task occurs. Called by
     * the parser to set location information.
     *
     * @param Location $location The location object describing the position of this
     *                           task within the buildfile.
     */
    public function setLocation(Location $location)
    {
        $this->location = $location;
    }

    /**
     * Returns the wrapper object for runtime configuration
     *
     * @return RuntimeConfigurable The wrapper object used by this task
     */
    public function getRuntimeConfigurableWrapper()
    {
        if ($this->wrapper === null) {
            $this->wrapper = new RuntimeConfigurable($this, $this->getTaskName());
        }

        return $this->wrapper;
    }

    /**
     *  Sets the wrapper object this task should use for runtime
     *  configurable elements.
     *
     * @param RuntimeConfigurable $wrapper The wrapper object this task should use
     */
    public function setRuntimeConfigurableWrapper(RuntimeConfigurable $wrapper)
    {
        $this->wrapper = $wrapper;
    }

    /**
     *  Configure this task if it hasn't been done already.
     */
    public function maybeConfigure()
    {
        if ($this->wrapper !== null) {
            $this->wrapper->maybeConfigure($this->project);
        }
    }

    /**
     * Perfrom this task
     *
     * @throws BuildException
     */
    public function perform()
    {

        try { // try executing task
            $this->project->fireTaskStarted($this);
            $this->maybeConfigure();
            $this->main();
            $this->project->fireTaskFinished($this, $null = null);
        } catch (Exception $exc) {
            if ($exc instanceof BuildException) {
                if ($this->getLocation() !== null) {
                    $exc->setLocation($this->getLocation());
                }
            }
            $this->project->fireTaskFinished($this, $exc);
            throw $exc;
        }
    }
}
<?php
/*
 *  $Id: 66a06bb5f7df99f501c5fe7d427c39d7ea661051 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Use introspection to "adapt" an arbitrary ( not extending Task, but with
 * similar patterns).
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: 66a06bb5f7df99f501c5fe7d427c39d7ea661051 $
 * @package   phing
 */
class TaskAdapter extends Task
{

    /** target object */
    private $proxy;

    /**
     * Main entry point.
     * @throws BuildException
     * @throws Exception
     * @return void
     */
    public function main()
    {

        if (method_exists($this->proxy, "setProject")) {
            try { // try to set project
                $this->proxy->setProject($this->project);
            } catch (Exception $ex) {
                $this->log("Error setting project in " . get_class($this->proxy) . Project::MSG_ERR);
                throw new BuildException($ex);
            }
        } else {
            throw new Exception("Error setting project in class " . get_class($this->proxy));
        }

        if (method_exists($this->proxy, "main")) {
            try { //try to call main
                $this->proxy->main($this->project);
            } catch (BuildException $be) {
                throw $be;
            } catch (Exception $ex) {
                $this->log("Error in " . get_class($this->proxy), Project::MSG_ERR);
                throw new BuildException("Error in " . get_class($this->proxy), $ex);
            }
        } else {
            throw new BuildException("Your task-like class '" . get_class(
                    $this->proxy
                ) . "' does not have a main() method");
        }
    }

    /**
     * Set the target object.
     * @param  object $o
     * @return void
     */
    public function setProxy($o)
    {
        $this->proxy = $o;
    }

    /**
     * Gets the target object.
     * @return object
     */
    public function getProxy()
    {
        return $this->proxy;
    }

}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 *  Abstract interface for objects which can contain tasks (targets)
 *  Used to check if a class can contain tasks (via instanceof)
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 *
 * @package   phing
 */
interface TaskContainer
{
    /**
     * Adds a task to this task container. Must be implemented
     * by derived class
     *
     * @param Task $task The task to be added to the container.
     */
    public function addTask(Task $task);
}
<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * ApiGen task (http://apigen.org).
 *
 * @package   phing.tasks.ext.apigen
 * @author    Martin Srank <martin@smasty.net>
 * @author    Jaroslav Hanslík <kukulich@kukulich.cz>
 * @author    Lukáš Homza <lukashomza@gmail.com>
 * @since     2.4.10
 */
class ApiGenTask extends Task
{
    /**
     * Default ApiGen executable name.
     *
     * @var string
     */
    private $executable = 'apigen';

    /**
     * Default ApiGen action.
     *
     * @var string
     */
    private $action = 'generate';

    /**
     * Default ApiGen options.
     *
     * @var string
     */
    private $options = array();

    /**
     * Sets the ApiGen executable name.
     *
     * @param string $executable
     */
    public function setExecutable($executable)
    {
        $this->executable = (string) $executable;
    }

    /**
     * Sets the ApiGen action to be executed.
     *
     * @param string $action
     */
    public function setAction($action)
    {
        $this->action = (string) $action;
    }

    /**
     * Sets the config file name.
     *
     * @param string $config
     */
    public function setConfig($config)
    {
        $this->options['config'] = (string) $config;
    }

    /**
     * Sets source files or directories.
     *
     * @param string $source
     */
    public function setSource($source)
    {
        $this->options['source'] = explode(',', $source);
    }

    /**
     * Sets the destination directory.
     *
     * @param string $destination
     */
    public function setDestination($destination)
    {
        $this->options['destination'] = (string) $destination;
    }

    /**
     * Sets list of allowed file extensions.
     *
     * @param string $extensions
     */
    public function setExtensions($extensions)
    {
        $this->options['extensions'] = explode(',', $extensions);
    }

    /**
     * Sets masks (case sensitive) to exclude files or directories from processing.
     *
     * @param string $exclude
     */
    public function setExclude($exclude)
    {
        $this->options['exclude'] = explode(',', $exclude);
    }

    /**
     * Sets masks to exclude elements from documentation generating.
     *
     * @param string $skipDocPath
     */
    public function setSkipDocPath($skipDocPath)
    {
        $this->options['skip-doc-path'] = explode(',', $skipDocPath);
    }

    /**
     * Sets the character set of source files.
     *
     * @param string $charset
     */
    public function setCharset($charset)
    {
        $this->options['charset'] = explode(',', $charset);
    }

    /**
     * Sets the main project name prefix.
     *
     * @param string $main
     */
    public function setMain($main)
    {
        $this->options['main'] = (string) $main;
    }

    /**
     * Sets the title of generated documentation.
     *
     * @param string $title
     */
    public function setTitle($title)
    {
        $this->options['title'] = (string) $title;
    }

    /**
     * Sets the documentation base URL.
     *
     * @param string $baseUrl
     */
    public function setBaseUrl($baseUrl)
    {
        $this->options['base-url'] = (string) $baseUrl;
    }

    /**
     * Sets the Google Custom Search ID.
     *
     * @param string $googleCseId
     */
    public function setGoogleCseId($googleCseId)
    {
        $this->options['google-cse-id'] = (string) $googleCseId;
    }

    /**
     * Sets the Google Custom Search label.
     *
     * @param string $googleCseLabel
     */
    public function setGoogleCseLabel($googleCseLabel)
    {
        $this->options['google-cse-label'] = (string) $googleCseLabel;
    }

    /**
     * Sets the Google Analytics tracking code.
     *
     * @param string $googleAnalytics
     */
    public function setGoogleAnalytics($googleAnalytics)
    {
        $this->options['google-analytics'] = (string) $googleAnalytics;
    }

    /**
     * Sets the template config file name.
     *
     * @param string $templateConfig
     */
    public function setTemplateConfig($templateConfig)
    {
        $this->options['template-config'] = (string) $templateConfig;
    }

    /**
     * Sets the template config file name.
     *
     * @param string $templateTheme
     */
    public function setTemplateTheme($templateTheme)
    {
        $this->options['template-theme'] = (string) $templateTheme;
    }

    /**
     * Sets how elements should be grouped in the menu.
     *
     * @param string $groups
     */
    public function setGroups($groups)
    {
        $this->options['groups'] = (string) $groups;
    }

    /**
     * Sets the element access levels.
     *
     * Documentation only for methods and properties with the given access level will be generated.
     *
     * @param string $accessLevels
     */
    public function setAccessLevels($accessLevels)
    {
        $this->options['access-levels'] = (string) $accessLevels;
    }

    /**
     * Sets the element access levels.
     *
     * Documentation only for methods and properties with the given access level will be generated.
     *
     * @param string $annotationGroups
     */
    public function setAnnotationGroups($annotationGroups)
    {
        $this->options['annotation-groups'] = (string) $annotationGroups;
    }

    /**
     * Sets if documentation for elements marked as internal and internal documentation parts should be generated.
     *
     * @param boolean $internal
     */
    public function setInternal($internal)
    {
        if((bool) $internal) {
            $this->options['internal'] = null;
        }
    }

    /**
     * Sets if documentation for PHP internal classes should be generated.
     *
     * @param boolean $php
     */
    public function setPhp($php)
    {
        if((bool) $php) {
            $this->options['php'] = null;
        }
    }

    /**
     * Sets if tree view of classes, interfaces, traits and exceptions should be generated.
     *
     * @param boolean $tree
     */
    public function setTree($tree)
    {
        if((bool) $tree) {
            $this->options['tree'] = null;
        }
    }

    /**
     * Sets if documentation for deprecated elements should be generated.
     *
     * @param boolean $deprecated
     */
    public function setDeprecated($deprecated)
    {
        if((bool) $deprecated) {
            $this->options['deprecated'] = null;
        }
    }

    /**
     * Sets if documentation of tasks should be generated.
     *
     * @param boolean $todo
     */
    public function setTodo($todo)
    {
        if((bool) $todo) {
            $this->options['todo'] = null;
        }
    }

    /**
     * Sets if highlighted source code files should be generated.
     *
     * @param boolean $noSourceCode
     */
    public function setSourceCode($noSourceCode)
    {
        if(!((bool) $noSourceCode)) {
            $this->options['no-source-code'] = null;
        }
    }

    /**
     * Sets if highlighted source code files should not be generated.
     *
     * @deprecated 
     * @param boolean $noSourceCode
     */
    public function setNoSourceCode($noSourceCode)
    {
        $this->setSourceCode(!$noSourceCode);
    }

    /**
     * Sets if a link to download documentation as a ZIP archive should be generated.
     *
     * @param boolean $download
     */
    public function setDownload($download)
    {
        if((bool) $download) {
            $this->options['download'] = null;
        }
    }

    /**
     * Enables/disables the debug mode.
     *
     * @param boolean $debug
     */
    public function setDebug($debug)
    {
        if((bool) $debug) {
            $this->options['debug'] = null;
        }
    }

    /**
     * Runs ApiGen.
     *
     * @throws BuildException If something is wrong.
     * @see Task::main()
     */
    public function main()
    {
        if ('apigen' !== $this->executable && !is_file($this->executable)) {
            throw new BuildException(sprintf('Executable %s not found', $this->executable), $this->getLocation());
        }

        if (!empty($this->options['config'])) {
            // Config check
            if (!is_file($this->options['config'])) {
                throw new BuildException(sprintf(
                    'Config file %s doesn\'t exist',
                    $this->options['config']
                ), $this->getLocation());
            }
        } else {
            // Source check
            if (empty($this->options['source'])) {
                throw new BuildException('Source is not set', $this->getLocation());
            }
            // Destination check
            if (empty($this->options['destination'])) {
                throw new BuildException('Destination is not set', $this->getLocation());
            }
        }

        // Source check
        if (!empty($this->options['source'])) {
            foreach ($this->options['source'] as $source) {
                if (!file_exists($source)) {
                    throw new BuildException(sprintf('Source %s doesn\'t exist', $source), $this->getLocation());
                }
            }
        }

        // Execute ApiGen
        exec(escapeshellcmd($this->executable) . ' ' . escapeshellcmd($this->action) . ' ' . $this->constructArguments(), $output, $return);

        $logType = 0 === $return ? Project::MSG_INFO : Project::MSG_ERR;
        foreach ($output as $line) {
            $this->log($line, $logType);
        }
    }

    /**
     * Generates command line arguments for the ApiGen executable.
     *
     * @return string
     */
    protected function constructArguments()
    {
        $args = array();
        foreach ($this->options as $option => $value) {
            if (is_bool($value)) {
                $args[] = '--' . $option . '=' . ($value ? 'yes' : 'no');
            } elseif (is_array($value)) {
                foreach ($value as $v) {
                    $args[] = '--' . $option . '=' . escapeshellarg($v);
                }
            } else {
                $args[] = '--' . $option . '=' . escapeshellarg($value);
            }
        }

        return implode(' ', $args);
    }
}
<?php

/*
 *  $Id: 168d2a681e6182eb5930fc1943b81df1c4c1220c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once "phing/Task.php";

/**
 * @author Max Romanovsky <max.romanovsky@gmail.com>
 * @package phing.tasks.ext
 */
class AutoloaderTask extends Task
{

    const DEFAULT_AUTOLOAD_PATH = 'vendor/autoload.php';

    private $autoloaderPath = self::DEFAULT_AUTOLOAD_PATH;

    /**
     * @return string
     */
    public function getAutoloaderPath()
    {
        return $this->autoloaderPath;
    }

    /**
     * @param string $autoloaderPath
     */
    public function setAutoloaderPath($autoloaderPath)
    {
        $this->autoloaderPath = $autoloaderPath;
    }

    /**
     *  Called by the project to let the task do it's work. This method may be
     *  called more than once, if the task is invoked more than once. For
     *  example, if target1 and target2 both depend on target3, then running
     *  <em>phing target1 target2</em> will run all tasks in target3 twice.
     *
     *  Should throw a BuildException if someting goes wrong with the build
     *
     *  This is here. Must be overloaded by real tasks.
     */
    public function main()
    {
        if (is_dir($this->autoloaderPath) || !is_readable($this->autoloaderPath)) {
            throw new BuildException(sprintf(
                'Provided autoloader file "%s" is not a readable file',
                $this->autoloaderPath
            ));
        }
        $this->log('Loading autoloader from ' . $this->autoloaderPath);
        require_once $this->autoloaderPath;
    }

}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/Task.php';
include_once 'phing/BuildException.php';
include_once 'phing/lib/Capsule.php';
include_once 'phing/util/StringHelper.php';

/**
 * A phing task for generating output by using Capsule.
 *
 * This is based on the interface to TexenTask from Apache's Velocity engine.
 *
 * @author    Hans Lellelid <hans@xmpl.org>
 *
 * @package   phing.tasks.ext
 */
class CapsuleTask extends Task
{
    /**
     * Capsule "template" engine.
     * @var Capsule
     */
    protected $context;

    /**
     * Any vars assigned via the build file.
     * @var array AssignedVar[]
     */
    protected $assignedVars = array();

    /**
     * This is the control template that governs the output.
     * It may or may not invoke the services of worker
     * templates.
     * @var string
     */
    protected $controlTemplate;

    /**
     * This is where Velocity will look for templates
     * using the file template loader.
     * @var string
     */
    protected $templatePath;

    /**
     * This is where texen will place all the output
     * that is a product of the generation process.
     * @var string
     */
    protected $outputDirectory;

    /**
     * This is the file where the generated text
     * will be placed.
     * @var string
     */
    protected $outputFile;

    /**
     * <p>
     * These are properties that are fed into the
     * initial context from a properties file. This
     * is simply a convenient way to set some values
     * that you wish to make available in the context.
     * </p>
     * <p>
     * These values are not critical, like the template path
     * or output path, but allow a convenient way to
     * set a value that may be specific to a particular
     * generation task.
     * </p>
     * <p>
     * For example, if you are generating scripts to allow
     * user to automatically create a database, then
     * you might want the <code>$databaseName</code>
     * to be placed
     * in the initial context so that it is available
     * in a script that might look something like the
     * following:
     * <code><pre>
     * #!bin/sh
     *
     * echo y | mysqladmin create $databaseName
     * </pre></code>
     * The value of <code>$databaseName</code> isn't critical to
     * output, and you obviously don't want to change
     * the ant task to simply take a database name.
     * So initial context values can be set with
     * properties file.
     *
     * @var array
     */
    protected $contextProperties;

    // -----------------------------------------------------------------------
    // The following getters & setters are used by phing to set properties
    // specified in the XML for the capsule task.
    // -----------------------------------------------------------------------

    /**
     * [REQUIRED] Set the control template for the
     * generating process.
     * @param  string $controlTemplate
     * @return void
     */
    public function setControlTemplate($controlTemplate)
    {
        $this->controlTemplate = $controlTemplate;
    }

    /**
     * Get the control template for the
     * generating process.
     * @return string
     */
    public function getControlTemplate()
    {
        return $this->controlTemplate;
    }

    /**
     * [REQUIRED] Set the path where Velocity will look
     * for templates using the file template
     * loader.
     * @param $templatePath
     * @return void
     */
    public function setTemplatePath($templatePath)
    {
        $resolvedPath = "";
        $tok = strtok($templatePath, ",");
        while ($tok) {
            // resolve relative path from basedir and leave
            // absolute path untouched.
            $fullPath = $this->project->resolveFile($tok);
            $cpath = $fullPath->getCanonicalPath();
            if ($cpath === false) {
                $this->log("Template directory does not exist: " . $fullPath->getAbsolutePath());
            } else {
                $resolvedPath .= $cpath;
            }
            $tok = strtok(",");
            if ($tok) {
                $resolvedPath .= ",";
            }
        }
        $this->templatePath = $resolvedPath;
    }

    /**
     * Get the path where Velocity will look
     * for templates using the file template
     * loader.
     * @return string
     */
    public function getTemplatePath()
    {
        return $this->templatePath;
    }

    /**
     * [REQUIRED] Set the output directory. It will be
     * created if it doesn't exist.
     * @param  PhingFile $outputDirectory
     * @return void
     * @throws Exception
     */
    public function setOutputDirectory(PhingFile $outputDirectory)
    {
        try {
            if (!$outputDirectory->exists()) {
                $this->log(
                    "Output directory does not exist, creating: " . $outputDirectory->getPath(),
                    Project::MSG_VERBOSE
                );
                if (!$outputDirectory->mkdirs()) {
                    throw new IOException("Unable to create Ouptut directory: " . $outputDirectory->getAbsolutePath());
                }
            }
            $this->outputDirectory = $outputDirectory->getCanonicalPath();
        } catch (IOException $ioe) {
            throw new BuildException($ioe);
        }
    }

    /**
     * Get the output directory.
     * @return string
     */
    public function getOutputDirectory()
    {
        return $this->outputDirectory;
    }

    /**
     * [REQUIRED] Set the output file for the
     * generation process.
     * @param  string $outputFile (TODO: change this to File)
     * @return void
     */
    public function setOutputFile($outputFile)
    {
        $this->outputFile = $outputFile;
    }

    /**
     * Get the output file for the
     * generation process.
     * @return string
     */
    public function getOutputFile()
    {
        return $this->outputFile;
    }

    /**
     * Set the context properties that will be
     * fed into the initial context be the
     * generating process starts.
     * @param  string $file
     * @throws BuildException
     * @return void
     */
    public function setContextProperties($file)
    {
        $sources = explode(",", $file);
        $this->contextProperties = new Properties();

        // Always try to get the context properties resource
        // from a file first. Templates may be taken from a JAR
        // file but the context properties resource may be a
        // resource in the filesystem. If this fails than attempt
        // to get the context properties resource from the
        // classpath.
        for ($i = 0, $sourcesLength = count($sources); $i < $sourcesLength; $i++) {
            $source = new Properties();

            try {

                // resolve relative path from basedir and leave
                // absolute path untouched.
                $fullPath = $this->project->resolveFile($sources[$i]);
                $this->log("Using contextProperties file: " . $fullPath->toString());
                $source->load($fullPath);

            } catch (Exception $e) {

                throw new BuildException("Context properties file " . $sources[$i] .
                    " could not be found in the file system!");

            }

            $keys = $source->keys();

            foreach ($keys as $key) {
                $name = $key;
                $value = $this->project->replaceProperties($source->getProperty($name));
                $this->contextProperties->setProperty($name, $value);
            }
        }
    }

    /**
     * Get the context properties that will be
     * fed into the initial context be the
     * generating process starts.
     * @return Properties
     */
    public function getContextProperties()
    {
        return $this->contextProperties;
    }

    /**
     * Creates an "AssignedVar" class.
     */
    public function createAssign()
    {
        $a = new AssignedVar();
        $this->assignedVars[] = $a;

        return $a;
    }

    // ---------------------------------------------------------------
    // End of XML setters & getters
    // ---------------------------------------------------------------

    /**
     * Creates a Smarty object.
     *
     * @return Capsule   initialized (cleared) Smarty context.
     * @throws Exception the execute method will catch
     *                   and rethrow as a <code>BuildException</code>
     */
    public function initControlContext()
    {
        $this->context->clear();
        foreach ($this->assignedVars as $var) {
            $this->context->put($var->getName(), $var->getValue());
        }

        return $this->context;
    }

    /**
     * Execute the input script with Velocity
     *
     * @throws BuildException
     *                        BuildExceptions are thrown when required attributes are missing.
     *                        Exceptions thrown by Velocity are rethrown as BuildExceptions.
     */
    public function main()
    {

        // Make sure the template path is set.
        if (empty($this->templatePath)) {
            throw new BuildException("The template path needs to be defined!");
        }

        // Make sure the control template is set.
        if ($this->controlTemplate === null) {
            throw new BuildException("The control template needs to be defined!");
        }

        // Make sure the output directory is set.
        if ($this->outputDirectory === null) {
            throw new BuildException("The output directory needs to be defined!");
        }

        // Make sure there is an output file.
        if ($this->outputFile === null) {
            throw new BuildException("The output file needs to be defined!");
        }

        // Setup Smarty runtime.

        // Smarty uses one object to store properties and to store
        // the context for the template (unlike Velocity).  We setup this object, calling it
        // $this->context, and then initControlContext simply zeros out
        // any assigned variables.
        $this->context = new Capsule();

        if ($this->templatePath !== null) {
            $this->log("Using templatePath: " . $this->templatePath);
            $this->context->setTemplatePath($this->templatePath);
        }

        // Make sure the output directory exists, if it doesn't
        // then create it.
        $outputDir = new PhingFile($this->outputDirectory);
        if (!$outputDir->exists()) {
            $this->log("Output directory does not exist, creating: " . $outputDir->getAbsolutePath());
            $outputDir->mkdirs();
        }

        $this->context->setOutputDirectory($outputDir->getAbsolutePath());

        $path = $this->outputDirectory . DIRECTORY_SEPARATOR . $this->outputFile;
        $this->log("Generating to file " . $path);

        //$writer = new FileWriter($path);

        // The generator and the output path should
        // be placed in the init context here and
        // not in the generator class itself.
        $c = $this->initControlContext();

        // Set any variables that need to always
        // be loaded
        $this->populateInitialContext($c);

        // Feed all the options into the initial
        // control context so they are available
        // in the control/worker templates.
        if ($this->contextProperties !== null) {

            foreach ($this->contextProperties->keys() as $property) {

                $value = $this->contextProperties->getProperty($property);

                // Special exception (from Texen)
                // for properties ending in file.contents:
                // in that case we dump the contents of the file
                // as the "value" for the Property.
                if (preg_match('/file\.contents$/', $property)) {
                    // pull in contents of file specified

                    $property = substr($property, 0, strpos($property, "file.contents") - 1);

                    // reset value, and then
                    // read in the contents of the file into that var
                    $value = "";
                    $f = new PhingFile($this->project->resolveFile($value)->getCanonicalPath());
                    if ($f->exists()) {
                        $fr = new FileReader($f);
                        $fr->readInto($value);
                    }

                } // if ends with file.contents

                if (StringHelper::isBoolean($value)) {
                    $value = StringHelper::booleanValue($value);
                }

                $c->put($property, $value);

            } // foreach property

        } // if contextProperties !== null

        try {
            $this->log("Parsing control template: " . $this->controlTemplate);
            $c->parse($this->controlTemplate, $path);
        } catch (Exception $ioe) {
            throw new BuildException("Cannot write parsed template: " . $ioe->getMessage());
        }

        $this->cleanup();
    }

    /**
     * Place useful objects into the initial context.
     *
     *
     * @param  Capsule   $context The context to populate, as retrieved from
     *                            {@link #initControlContext()}.
     * @return void
     * @throws Exception Error while populating context.  The {@link
     *                           #main()} method will catch and rethrow as a
     *                           <code>BuildException</code>.
     */
    protected function populateInitialContext(Capsule $context)
    {
        $this->context->put("now", strftime("%c", time()));
        $this->context->put("task", $this);
    }

    /**
     * A hook method called at the end of {@link #execute()} which can
     * be overridden to perform any necessary cleanup activities (such
     * as the release of database connections, etc.).  By default,
     * does nothing.
     *
     * @return void
     */
    protected function cleanup()
    {
    }
}

/**
 * An "inner" class for holding assigned var values.
 * May be need to expand beyond name/value in the future.
 *
 * @package phing.tasks.ext
 */
class AssignedVar
{
    private $name;
    private $value;

    /**
     * @param string $v
     */
    public function setName($v)
    {
        $this->name = $v;
    }

    /**
     * @param mixed $v
     */
    public function setValue($v)
    {
        $this->value = $v;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @return mixed
     */
    public function getValue()
    {
        return $this->value;
    }
}
<?php

/*
 *  $Id: be79ded318bcd97deb5b1d7bac9f216dff9bbdfe $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once "phing/Task.php";
require_once "phing/types/Commandline.php";

/**
 * Composer Task
 * Run composer straight from phing
 *
 * @author nuno costa <nuno@francodacosta.com>
 * @license MIT
 * @version $Id: be79ded318bcd97deb5b1d7bac9f216dff9bbdfe $
 * @package phing.tasks.ext
 */
class ComposerTask extends Task
{
    /**
     * @var string the path to php interpreter
     */
    private $php = '';

    /**
     *
     * @var string the Composer command to execute
     */
    private $command = null;

    /**
     *
     * @var Commandline
     */
    private $commandLine = null;
    /**
     *
     * @var string path to Composer application
     */
    private $composer = 'composer.phar';

    /**
     *
     */
    public function __construct()
    {
        $this->commandLine = new Commandline();
    }

    /**
     * Initialize the interpreter with the Phing property php.interpreter
     */
    public function init()
    {
        $this->setPhp($this->project->getProperty('php.interpreter'));
    }

    /**
     * Sets the path to php executable.
     *
     * @param string $php
     */
    public function setPhp($php)
    {
        $this->php = $php;
    }

    /**
     * gets the path to php executable.
     *
     * @return string
     */
    public function getPhp()
    {
        return $this->php;
    }

    /**
     * sets the Composer command to execute
     * @param string $command
     */
    public function setCommand($command)
    {
        $this->command = $command;
    }

    /**
     * return the Composer command to execute
     * @return String
     */
    public function getCommand()
    {
        return $this->command;
    }

    /**
     * sets the path to Composer application
     * @param string $console
     */
    public function setComposer($console)
    {
        $this->composer = $console;
    }

    /**
     * returns the path to Composer application
     * @return string
     */
    public function getComposer()
    {
        return $this->composer;
    }

    /**
     * creates a nested arg task
     *
     * @return Arg Argument object
     */

    public function createArg()
    {
        return $this->commandLine->createArgument();
    }

    /**
     * Prepares the command string to be executed
     * @return string
     */
    private function prepareCommandLine()
    {
        $this->commandLine->setExecutable($this->getPhp());
        //We are un-shifting arguments to the beginning of the command line because arguments should be at the end
        $this->commandLine->createArgument(true)->setValue($this->getCommand());
        $this->commandLine->createArgument(true)->setValue($this->getComposer());
        $commandLine = strval($this->commandLine);
        //Creating new Commandline instance. It allows to handle subsequent calls correctly
        $this->commandLine = new Commandline();

        return $commandLine;
    }

    /**
     * executes the Composer task
     */
    public function main()
    {
        $commandLine = $this->prepareCommandLine();
        $this->log("executing " . $commandLine);

        $composerFile = new SplFileInfo($this->getComposer());
        if (false === $composerFile->isFile()) {
            throw new BuildException(sprintf('Composer binary not found, path is "%s"', $composerFile));
        }

        $return = 0;
        passthru($commandLine, $return);

        if ($return > 0) {
            throw new BuildException("Composer execution failed");
        }
    }
}
<?php
/**
 * $Id: 18a66018595b27816f2bd0ac5f2d67a42ea16416 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/util/Properties.php';

/**
 * Saves coverage output of the test to a specified database
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 18a66018595b27816f2bd0ac5f2d67a42ea16416 $
 * @package phing.tasks.ext.coverage
 * @since 2.1.0
 */
class CoverageMerger
{
    /**
     * @param $left
     * @param $right
     * @return array
     */
    private static function mergeCodeCoverage($left, $right)
    {
        $coverageMerged = array();

        reset($left);
        reset($right);

        while (current($left) !== false && current($right) !== false) {
            $linenr_left = key($left);
            $linenr_right = key($right);

            if ($linenr_left < $linenr_right) {
                $coverageMerged[$linenr_left] = current($left);
                next($left);
            } elseif ($linenr_right < $linenr_left) {
                $coverageMerged[$linenr_right] = current($right);
                next($right);
            } else {
                if ((current($left) < 0) || (current($right) < 0)) {
                    $coverageMerged[$linenr_right] = current($right);
                } else {
                    $coverageMerged[$linenr_right] = current($left) + current($right);
                }

                next($left);
                next($right);
            }
        }

        while (current($left) !== false) {
            $coverageMerged[key($left)] = current($left);
            next($left);
        }

        while (current($right) !== false) {
            $coverageMerged[key($right)] = current($right);
            next($right);
        }

        return $coverageMerged;
    }

    /**
     * @param  Project        $project
     * @return Properties
     * @throws BuildException
     */
    protected static function _getDatabase($project)
    {
        $coverageDatabase = $project->getProperty('coverage.database');

        if (!$coverageDatabase) {
            throw new BuildException("Property coverage.database is not set - please include coverage-setup in your build file");
        }

        $database = new PhingFile($coverageDatabase);

        $props = new Properties();
        $props->load($database);

        return $props;
    }

    /**
     * @param $project
     * @return array
     * @throws BuildException
     */
    public static function getWhiteList($project)
    {
        $whitelist = array();
        $props = self::_getDatabase($project);

        foreach ($props->getProperties() as $property) {
            $data = unserialize($property);
            $whitelist[] = $data['fullname'];
        }

        return $whitelist;
    }

    /**
     * @param $project
     * @param $codeCoverageInformation
     * @throws BuildException
     * @throws IOException
     */
    public static function merge($project, $codeCoverageInformation)
    {
        $props = self::_getDatabase($project);

        $coverageTotal = $codeCoverageInformation;

        foreach ($coverageTotal as $filename => $data) {
            $lines = array();
            $filename = strtolower($filename);

            if ($props->getProperty($filename) != null) {
                foreach ($data as $_line => $_data) {
                    if ($_data === null) {
                        continue;
                    }

                    if (is_array($_data)) {
                        $count = count($_data);
                        if ($count == 0) {
                            $count = -1;
                        }
                    } else {
                        if ($_data == -1) {
                            // not executed
                            $count = -1;
                        } else {
                            if ($_data == -2) {
                                // dead code
                                $count = -2;
                            }
                        }
                    }

                    $lines[$_line] = $count;
                }

                ksort($lines);

                $file = unserialize($props->getProperty($filename));
                $left = $file['coverage'];

                $coverageMerged = CoverageMerger::mergeCodeCoverage($left, $lines);

                $file['coverage'] = $coverageMerged;
                $props->setProperty($filename, serialize($file));
            }
        }

        $props->store();
    }
}
<?php
/**
 * $Id: 6d4732d2b352c992dc03cd92cd83ad1b81bf9e45 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/system/io/PhingFile.php';
require_once 'phing/system/io/Writer.php';
require_once 'phing/system/util/Properties.php';
require_once 'phing/tasks/ext/coverage/CoverageMerger.php';

/**
 * Merges code coverage snippets into a code coverage database
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 6d4732d2b352c992dc03cd92cd83ad1b81bf9e45 $
 * @package phing.tasks.ext.coverage
 * @since 2.1.0
 */
class CoverageMergerTask extends Task
{
    /** the list of filesets containing the .php filename rules */
    private $filesets = array();

    /**
     * Add a new fileset containing the .php files to process
     *
     * @param FileSet the new fileset containing .php files
     */
    public function addFileSet(FileSet $fileset)
    {
        $this->filesets[] = $fileset;
    }

    /**
     * Iterate over all filesets and return all the filenames.
     *
     * @return array an array of filenames
     */
    private function getFilenames()
    {
        $files = array();

        foreach ($this->filesets as $fileset) {
            $ds = $fileset->getDirectoryScanner($this->project);
            $ds->scan();

            $includedFiles = $ds->getIncludedFiles();

            foreach ($includedFiles as $file) {
                $fs = new PhingFile(basename($ds->getBaseDir()), $file);

                $files[] = $fs->getAbsolutePath();
            }
        }

        return $files;
    }

    public function main()
    {
        $files = $this->getFilenames();

        $this->log("Merging " . count($files) . " coverage files");

        foreach ($files as $file) {
            $coverageInformation = unserialize(file_get_contents($file));

            CoverageMerger::merge($this->project, array($coverageInformation));
        }
    }
}
<?php
/**
 * $Id: 9a15437e8ba7eb26f59aa20fd6c10e682b26e697 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/system/io/PhingFile.php';
require_once 'phing/system/io/Writer.php';
require_once 'phing/system/util/Properties.php';
require_once 'phing/tasks/ext/phpunit/PHPUnitUtil.php';
require_once 'phing/tasks/ext/coverage/CoverageReportTransformer.php';

/**
 * Transforms information in a code coverage database to XML
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 9a15437e8ba7eb26f59aa20fd6c10e682b26e697 $
 * @package phing.tasks.ext.coverage
 * @since 2.1.0
 */
class CoverageReportTask extends Task
{
    private $outfile = "coverage.xml";

    private $transformers = array();

    /** the classpath to use (optional) */
    private $classpath = null;

    /** the path to the GeSHi library (optional) */
    private $geshipath = "";

    /** the path to the GeSHi language files (optional) */
    private $geshilanguagespath = "";

    /**
     * @param Path $classpath
     */
    public function setClasspath(Path $classpath)
    {
        if ($this->classpath === null) {
            $this->classpath = $classpath;
        } else {
            $this->classpath->append($classpath);
        }
    }

    /**
     * @return null|Path
     */
    public function createClasspath()
    {
        $this->classpath = new Path();

        return $this->classpath;
    }

    /**
     * @param $path
     */
    public function setGeshiPath($path)
    {
        $this->geshipath = $path;
    }

    /**
     * @param $path
     */
    public function setGeshiLanguagesPath($path)
    {
        $this->geshilanguagespath = $path;
    }

    /**
     *
     */
    public function __construct()
    {
        $this->doc = new DOMDocument();
        $this->doc->encoding = 'UTF-8';
        $this->doc->formatOutput = true;
        $this->doc->appendChild($this->doc->createElement('snapshot'));
    }

    /**
     * @param $outfile
     */
    public function setOutfile($outfile)
    {
        $this->outfile = $outfile;
    }

    /**
     * Generate a report based on the XML created by this task
     */
    public function createReport()
    {
        $transformer = new CoverageReportTransformer($this);
        $this->transformers[] = $transformer;

        return $transformer;
    }

    /**
     * @param $packageName
     * @return null
     */
    protected function getPackageElement($packageName)
    {
        $packages = $this->doc->documentElement->getElementsByTagName('package');

        foreach ($packages as $package) {
            if ($package->getAttribute('name') == $packageName) {
                return $package;
            }
        }

        return null;
    }

    /**
     * @param $packageName
     * @param $element
     */
    protected function addClassToPackage($packageName, $element)
    {
        $package = $this->getPackageElement($packageName);

        if ($package === null) {
            $package = $this->doc->createElement('package');
            $package->setAttribute('name', $packageName);
            $this->doc->documentElement->appendChild($package);
        }

        $package->appendChild($element);
    }

    /**
     * Adds a subpackage to their package
     *
     * @param string $packageName    The name of the package
     * @param string $subpackageName The name of the subpackage
     *
     * @author Benjamin Schultz <bschultz@proqrent.de>
     * @return void
     */
    protected function addSubpackageToPackage($packageName, $subpackageName)
    {
        $package = $this->getPackageElement($packageName);
        $subpackage = $this->getSubpackageElement($subpackageName);

        if ($package === null) {
            $package = $this->doc->createElement('package');
            $package->setAttribute('name', $packageName);
            $this->doc->documentElement->appendChild($package);
        }

        if ($subpackage === null) {
            $subpackage = $this->doc->createElement('subpackage');
            $subpackage->setAttribute('name', $subpackageName);
        }

        $package->appendChild($subpackage);
    }

    /**
     * Returns the subpackage element
     *
     * @param string $subpackageName The name of the subpackage
     *
     * @author Benjamin Schultz <bschultz@proqrent.de>
     * @return DOMNode|null null when no DOMNode with the given name exists
     */
    protected function getSubpackageElement($subpackageName)
    {
        $subpackages = $this->doc->documentElement->getElementsByTagName('subpackage');

        foreach ($subpackages as $subpackage) {
            if ($subpackage->getAttribute('name') == $subpackageName) {
                return $subpackage;
            }
        }

        return null;
    }

    /**
     * Adds a class to their subpackage
     *
     * @param string  $classname The name of the class
     * @param DOMNode $element   The dom node to append to the subpackage element
     *
     * @author Benjamin Schultz <bschultz@proqrent.de>
     * @return void
     */
    protected function addClassToSubpackage($classname, $element)
    {
        $subpackageName = PHPUnitUtil::getSubpackageName($classname);

        $subpackage = $this->getSubpackageElement($subpackageName);

        if ($subpackage === null) {
            $subpackage = $this->doc->createElement('subpackage');
            $subpackage->setAttribute('name', $subpackageName);
            $this->doc->documentElement->appendChild($subpackage);
        }

        $subpackage->appendChild($element);
    }

    /**
     * @param $source
     * @return string
     */
    protected function stripDiv($source)
    {
        $openpos = strpos($source, "<div");
        $closepos = strpos($source, ">", $openpos);

        $line = substr($source, $closepos + 1);

        $tagclosepos = strpos($line, "</div>");

        $line = substr($line, 0, $tagclosepos);

        return $line;
    }

    /**
     * @param $filename
     * @return array
     */
    protected function highlightSourceFile($filename)
    {
        if ($this->geshipath) {
            require_once $this->geshipath . '/geshi.php';

            $source = file_get_contents($filename);

            $geshi = new GeSHi($source, 'php', $this->geshilanguagespath);

            $geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS);

            $geshi->enable_strict_mode(true);

            $geshi->enable_classes(true);

            $geshi->set_url_for_keyword_group(3, '');

            $html = $geshi->parse_code();

            $lines = preg_split("#</?li>#", $html);

            // skip first and last line
            array_pop($lines);
            array_shift($lines);

            $lines = array_filter($lines);

            $lines = array_map(array($this, 'stripDiv'), $lines);

            return $lines;
        } else {
            $lines = file($filename);

            for ($i = 0; $i < count($lines); $i++) {
                $line = $lines[$i];

                $line = rtrim($line);

                if (function_exists('mb_check_encoding') && mb_check_encoding($line, 'UTF-8')) {
                    $lines[$i] = $line;
                } else {
                    if (function_exists('mb_convert_encoding')) {
                        $lines[$i] = mb_convert_encoding($line, 'UTF-8');
                    } else {
                        $lines[$i] = utf8_encode($line);
                    }
                }
            }

            return $lines;
        }
    }

    /**
     * @param $filename
     * @param $coverageInformation
     * @param int $classStartLine
     * @return DOMElement
     */
    protected function transformSourceFile($filename, $coverageInformation, $classStartLine = 1)
    {
        $sourceElement = $this->doc->createElement('sourcefile');
        $sourceElement->setAttribute('name', basename($filename));

        /**
         * Add original/full filename to document
         */
        $sourceElement->setAttribute('sourcefile', $filename);

        $filelines = $this->highlightSourceFile($filename);

        $linenr = 1;

        foreach ($filelines as $line) {
            $lineElement = $this->doc->createElement('sourceline');
            $lineElement->setAttribute(
                'coveredcount',
                (isset($coverageInformation[$linenr]) ? $coverageInformation[$linenr] : '0')
            );

            if ($linenr == $classStartLine) {
                $lineElement->setAttribute('startclass', 1);
            }

            $textnode = $this->doc->createTextNode($line);
            $lineElement->appendChild($textnode);

            $sourceElement->appendChild($lineElement);

            $linenr++;
        }

        return $sourceElement;
    }

    /**
     * Transforms the coverage information
     *
     * @param string $filename            The filename
     * @param array  $coverageInformation Array with covergae information
     *
     * @author Michiel Rook <mrook@php.net>
     * @author Benjamin Schultz <bschultz@proqrent.de>
     * @return void
     */
    protected function transformCoverageInformation($filename, $coverageInformation)
    {
        $classes = PHPUnitUtil::getDefinedClasses($filename, $this->classpath);

        if (is_array($classes)) {
            foreach ($classes as $classname) {
                $reflection = new ReflectionClass($classname);
                $methods = $reflection->getMethods();

                if (method_exists($reflection, 'getShortName')) {
                    $className = $reflection->getShortName();
                } else {
                    $className = $reflection->getName();
                }

                $classElement = $this->doc->createElement('class');
                $classElement->setAttribute('name', $className);

                $packageName = PHPUnitUtil::getPackageName($reflection->getName());
                $subpackageName = PHPUnitUtil::getSubpackageName($reflection->getName());

                if ($subpackageName !== null) {
                    $this->addSubpackageToPackage($packageName, $subpackageName);
                    $this->addClassToSubpackage($reflection->getName(), $classElement);
                } else {
                    $this->addClassToPackage($packageName, $classElement);
                }

                $classStartLine = $reflection->getStartLine();

                $methodscovered = 0;
                $methodcount = 0;

                // Strange PHP5 reflection bug, classes without parent class or implemented interfaces seem to start one line off
                if ($reflection->getParentClass() == null && count($reflection->getInterfaces()) == 0) {
                    unset($coverageInformation[$classStartLine + 1]);
                } else {
                    unset($coverageInformation[$classStartLine]);
                }

                // Remove out-of-bounds info
                unset($coverageInformation[0]);

                reset($coverageInformation);

                foreach ($methods as $method) {
                    // PHP5 reflection considers methods of a parent class to be part of a subclass, we don't
                    if ($method->getDeclaringClass()->getName() != $reflection->getName()) {
                        continue;
                    }

                    // small fix for XDEBUG_CC_UNUSED
                    if (isset($coverageInformation[$method->getStartLine()])) {
                        unset($coverageInformation[$method->getStartLine()]);
                    }

                    if (isset($coverageInformation[$method->getEndLine()])) {
                        unset($coverageInformation[$method->getEndLine()]);
                    }

                    if ($method->isAbstract()) {
                        continue;
                    }

                    $linenr = key($coverageInformation);

                    while ($linenr !== null && $linenr < $method->getStartLine()) {
                        next($coverageInformation);
                        $linenr = key($coverageInformation);
                    }

                    $methodCoveredCount = 0;
                    $methodTotalCount = 0;

                    $methodHasCoveredLine = false;

                    while ($linenr !== null && $linenr <= $method->getEndLine()) {
                        $methodTotalCount++;
                        $methodHasCoveredLine = true;

                        // set covered when CODE is other than -1 (not executed)
                        if ($coverageInformation[$linenr] > 0 || $coverageInformation[$linenr] == -2) {
                            $methodCoveredCount++;
                        }

                        next($coverageInformation);
                        $linenr = key($coverageInformation);
                    }

                    if (($methodTotalCount == $methodCoveredCount) && $methodHasCoveredLine) {
                        $methodscovered++;
                    }

                    $methodcount++;
                }

                $statementcount = count(
                    array_filter(
                        $coverageInformation,
                        create_function('$var', 'return ($var != -2);')
                    )
                );

                $statementscovered = count(
                    array_filter(
                        $coverageInformation,
                        create_function('$var', 'return ($var >= 0);')
                    )
                );

                $classElement->appendChild(
                    $this->transformSourceFile($filename, $coverageInformation, $classStartLine)
                );

                $classElement->setAttribute('methodcount', $methodcount);
                $classElement->setAttribute('methodscovered', $methodscovered);
                $classElement->setAttribute('statementcount', $statementcount);
                $classElement->setAttribute('statementscovered', $statementscovered);
                $classElement->setAttribute('totalcount', $methodcount + $statementcount);
                $classElement->setAttribute('totalcovered', $methodscovered + $statementscovered);
            }
        }
    }

    protected function calculateStatistics()
    {
        $packages = $this->doc->documentElement->getElementsByTagName('package');

        $totalmethodcount = 0;
        $totalmethodscovered = 0;

        $totalstatementcount = 0;
        $totalstatementscovered = 0;

        foreach ($packages as $package) {
            $methodcount = 0;
            $methodscovered = 0;

            $statementcount = 0;
            $statementscovered = 0;

            $subpackages = $package->getElementsByTagName('subpackage');

            foreach ($subpackages as $subpackage) {
                $subpackageMethodCount = 0;
                $subpackageMethodsCovered = 0;

                $subpackageStatementCount = 0;
                $subpackageStatementsCovered = 0;

                $subpackageClasses = $subpackage->getElementsByTagName('class');

                foreach ($subpackageClasses as $subpackageClass) {
                    $subpackageMethodCount += $subpackageClass->getAttribute('methodcount');
                    $subpackageMethodsCovered += $subpackageClass->getAttribute('methodscovered');

                    $subpackageStatementCount += $subpackageClass->getAttribute('statementcount');
                    $subpackageStatementsCovered += $subpackageClass->getAttribute('statementscovered');
                }

                $subpackage->setAttribute('methodcount', $subpackageMethodCount);
                $subpackage->setAttribute('methodscovered', $subpackageMethodsCovered);

                $subpackage->setAttribute('statementcount', $subpackageStatementCount);
                $subpackage->setAttribute('statementscovered', $subpackageStatementsCovered);

                $subpackage->setAttribute('totalcount', $subpackageMethodCount + $subpackageStatementCount);
                $subpackage->setAttribute('totalcovered', $subpackageMethodsCovered + $subpackageStatementsCovered);
            }

            $classes = $package->getElementsByTagName('class');

            foreach ($classes as $class) {
                $methodcount += $class->getAttribute('methodcount');
                $methodscovered += $class->getAttribute('methodscovered');

                $statementcount += $class->getAttribute('statementcount');
                $statementscovered += $class->getAttribute('statementscovered');
            }

            $package->setAttribute('methodcount', $methodcount);
            $package->setAttribute('methodscovered', $methodscovered);

            $package->setAttribute('statementcount', $statementcount);
            $package->setAttribute('statementscovered', $statementscovered);

            $package->setAttribute('totalcount', $methodcount + $statementcount);
            $package->setAttribute('totalcovered', $methodscovered + $statementscovered);

            $totalmethodcount += $methodcount;
            $totalmethodscovered += $methodscovered;

            $totalstatementcount += $statementcount;
            $totalstatementscovered += $statementscovered;
        }

        $this->doc->documentElement->setAttribute('methodcount', $totalmethodcount);
        $this->doc->documentElement->setAttribute('methodscovered', $totalmethodscovered);

        $this->doc->documentElement->setAttribute('statementcount', $totalstatementcount);
        $this->doc->documentElement->setAttribute('statementscovered', $totalstatementscovered);

        $this->doc->documentElement->setAttribute('totalcount', $totalmethodcount + $totalstatementcount);
        $this->doc->documentElement->setAttribute('totalcovered', $totalmethodscovered + $totalstatementscovered);
    }

    public function main()
    {
        $coverageDatabase = $this->project->getProperty('coverage.database');

        if (!$coverageDatabase) {
            throw new BuildException("Property coverage.database is not set - please include coverage-setup in your build file");
        }

        $database = new PhingFile($coverageDatabase);

        $this->log("Transforming coverage report");

        $props = new Properties();
        $props->load($database);

        foreach ($props->keys() as $filename) {
            $file = unserialize($props->getProperty($filename));

            $this->transformCoverageInformation($file['fullname'], $file['coverage']);
        }

        $this->calculateStatistics();

        $this->doc->save($this->outfile);

        foreach ($this->transformers as $transformer) {
            $transformer->setXmlDocument($this->doc);
            $transformer->transform();
        }
    }
}
<?php
/**
 * $Id: d3a5fbec986f8d4d1fde4875192fb243c82f1e68 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/system/io/PhingFile.php';
require_once 'phing/system/io/FileWriter.php';
require_once 'phing/util/ExtendedFileStream.php';

/**
 * Transform a Phing/Xdebug code coverage xml report.
 * The default transformation generates an html report in framed style.
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: d3a5fbec986f8d4d1fde4875192fb243c82f1e68 $
 * @package phing.tasks.ext.coverage
 * @since 2.1.0
 */
class CoverageReportTransformer
{
    private $task = null;
    private $styleDir = "";

    /**
     * @var PhingFile
     */
    private $toDir = "";

    private $document = null;

    /** title of the project, used in the coverage report */
    private $title = "";

    /**
     * Whether to use the sorttable JavaScript library, defaults to false
     * See {@link http://www.kryogenix.org/code/browser/sorttable/)}
     *
     * @var boolean
     */
    private $useSortTable = false;

    /**
     * @param Task $task
     */
    public function __construct(Task $task)
    {
        $this->task = $task;
    }

    /**
     * @param $styleDir
     */
    public function setStyleDir($styleDir)
    {
        $this->styleDir = $styleDir;
    }

    /**
     * @param PhingFile $toDir
     */
    public function setToDir(PhingFile $toDir)
    {
        $this->toDir = $toDir;
    }

    /**
     * @param $document
     */
    public function setXmlDocument($document)
    {
        $this->document = $document;
    }

    /**
     * Setter for title parameter
     * @param $title
     */
    public function setTitle($title)
    {
        $this->title = $title;
    }

    /**
     * Sets whether to use the sorttable JavaScript library, defaults to false
     * See {@link http://www.kryogenix.org/code/browser/sorttable/)}
     *
     * @param boolean $useSortTable
     */
    public function setUseSortTable($useSortTable)
    {
        $this->useSortTable = (boolean) $useSortTable;
    }

    public function transform()
    {
        if (!$this->toDir->exists()) {
            throw new BuildException("Directory '" . $this->toDir . "' does not exist");
        }

        $xslfile = $this->getStyleSheet();

        $xsl = new DOMDocument();
        $xsl->load($xslfile->getAbsolutePath());

        $proc = new XSLTProcessor();
        if (defined('XSL_SECPREF_WRITE_FILE')) {
            if (version_compare(PHP_VERSION, '5.4', "<")) {
                ini_set("xsl.security_prefs", XSL_SECPREF_WRITE_FILE | XSL_SECPREF_CREATE_DIRECTORY);
            } else {
                $proc->setSecurityPrefs(XSL_SECPREF_WRITE_FILE | XSL_SECPREF_CREATE_DIRECTORY);
            }
        }

        $proc->importStyleSheet($xsl);

        ExtendedFileStream::registerStream();

        $toDir = (string) $this->toDir;

        // urlencode() the path if we're on Windows
        if (FileSystem::getFileSystem()->getSeparator() == '\\') {
            $toDir = urlencode($toDir);
        }

        // no output for the framed report
        // it's all done by extension...
        $proc->setParameter('', 'output.dir', $toDir);

        $proc->setParameter('', 'output.sorttable', $this->useSortTable);
        $proc->setParameter('', 'document.title', $this->title);
        $proc->transformToXML($this->document);

        ExtendedFileStream::unregisterStream();
    }

    /**
     * @return PhingFile
     * @throws BuildException
     */
    private function getStyleSheet()
    {
        $xslname = "coverage-frames.xsl";

        if ($this->styleDir) {
            $file = new PhingFile($this->styleDir, $xslname);
        } else {
            $path = Phing::getResourcePath("phing/etc/$xslname");

            if ($path === null) {
                $path = Phing::getResourcePath("etc/$xslname");

                if ($path === null) {
                    throw new BuildException("Could not find $xslname in resource path");
                }
            }

            $file = new PhingFile($path);
        }

        if (!$file->exists()) {
            throw new BuildException("Could not find file " . $file->getPath());
        }

        return $file;
    }
}
<?php
/**
 * $Id: bde7020012ba4f24e0d97768c76a5a8af99a6fbb $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/system/io/PhingFile.php';
require_once 'phing/system/io/Writer.php';
require_once 'phing/system/util/Properties.php';
require_once 'phing/tasks/ext/coverage/CoverageMerger.php';

/**
 * Initializes a code coverage database
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: bde7020012ba4f24e0d97768c76a5a8af99a6fbb $
 * @package phing.tasks.ext.coverage
 * @since 2.1.0
 */
class CoverageSetupTask extends Task
{
    /** the list of filesets containing the .php filename rules */
    private $filesets = array();

    /** Any filelists of files containing the .php filenames */
    private $filelists = array();

    /** the filename of the coverage database */
    private $database = "coverage.db";

    /** the classpath to use (optional) */
    private $classpath = null;

    /**
     * Add a new fileset containing the .php files to process
     *
     * @param FileSet the new fileset containing .php files
     */
    public function addFileSet(FileSet $fileset)
    {
        $this->filesets[] = $fileset;
    }

    /**
     * Supports embedded <filelist> element.
     * @return FileList
     */
    public function createFileList()
    {
        $num = array_push($this->filelists, new FileList());

        return $this->filelists[$num - 1];
    }

    /**
     * Sets the filename of the coverage database to use
     *
     * @param string the filename of the database
     */
    public function setDatabase($database)
    {
        $this->database = $database;
    }

    /**
     * @param Path $classpath
     */
    public function setClasspath(Path $classpath)
    {
        if ($this->classpath === null) {
            $this->classpath = $classpath;
        } else {
            $this->classpath->append($classpath);
        }
    }

    /**
     * @return null|Path
     */
    public function createClasspath()
    {
        $this->classpath = new Path();

        return $this->classpath;
    }

    /**
     * Iterate over all filesets and return the filename of all files.
     *
     * @return array an array of (basedir, filenames) pairs
     */
    private function getFilenames()
    {
        $files = array();

        foreach ($this->filelists as $fl) {
            try {
                $list = $fl->getFiles($this->project);
                foreach ($list as $file) {
                    $fs = new PhingFile(strval($fl->getDir($this->project)), $file);
                    $files[] = array('key' => strtolower($fs->getAbsolutePath()), 'fullname' => $fs->getAbsolutePath());
                }
            } catch (BuildException $be) {
                $this->log($be->getMessage(), Project::MSG_WARN);
            }
        }

        foreach ($this->filesets as $fileset) {
            $ds = $fileset->getDirectoryScanner($this->project);
            $ds->scan();

            $includedFiles = $ds->getIncludedFiles();

            foreach ($includedFiles as $file) {
                $fs = new PhingFile(realpath($ds->getBaseDir()), $file);

                $files[] = array('key' => strtolower($fs->getAbsolutePath()), 'fullname' => $fs->getAbsolutePath());
            }
        }

        return $files;
    }

    public function init()
    {
    }

    public function main()
    {
        $files = $this->getFilenames();

        $this->log("Setting up coverage database for " . count($files) . " files");

        $props = new Properties();

        foreach ($files as $file) {
            $fullname = $file['fullname'];
            $filename = $file['key'];

            $props->setProperty($filename, serialize(array('fullname' => $fullname, 'coverage' => array())));
        }

        $dbfile = new PhingFile($this->database);

        $props->store($dbfile);

        $this->project->setProperty('coverage.database', $dbfile->getAbsolutePath());
    }
}
<?php
/**
 * $Id: 1cf68f1a4ec652811a5fd9db8353cae9a88ee03e $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/system/io/PhingFile.php';
require_once 'phing/system/util/Properties.php';
require_once 'phing/types/Excludes.php';

/**
 * Stops the build if any of the specified coverage threshold was not reached
 *
 * @author  Benjamin Schultz <bschultz@proqrent.de>
 * @version $Id: 1cf68f1a4ec652811a5fd9db8353cae9a88ee03e $
 * @package phing.tasks.ext.coverage
 * @since   2.4.1
 */
class CoverageThresholdTask extends Task
{
    /**
     * Holds an optional classpath
     *
     * @var Path
     */
    private $_classpath = null;

    /**
     * Holds the exclusions
     *
     * @var Excludes
     */
    private $_excludes = null;

    /**
     * Holds an optional database file
     *
     * @var PhingFile
     */
    private $_database = null;

    /**
     * Holds the coverage threshold for the entire project
     *
     * @var integer
     */
    private $_perProject = 25;

    /**
     * Holds the coverage threshold for any class
     *
     * @var integer
     */
    private $_perClass = 25;

    /**
     * Holds the coverage threshold for any method
     *
     * @var integer
     */
    private $_perMethod = 25;

    /**
     * Holds the minimum found coverage value for a class
     *
     * @var integer
     */
    private $_minClassCoverageFound = null;

    /**
     * Holds the minimum found coverage value for a method
     *
     * @var integer
     */
    private $_minMethodCoverageFound = null;

    /**
     * Number of statements in the entire project
     *
     * @var integer
     */
    private $_projectStatementCount = 0;

    /**
     * Number of covered statements in the entire project
     *
     * @var integer
     */
    private $_projectStatementsCovered = 0;

    /**
     * Whether to enable detailed logging
     *
     * @var boolean
     */
    private $_verbose = false;

    /**
     * Sets an optional classpath
     *
     * @param Path $classpath The classpath
     */
    public function setClasspath(Path $classpath)
    {
        if ($this->_classpath === null) {
            $this->_classpath = $classpath;
        } else {
            $this->_classpath->append($classpath);
        }
    }

    /**
     * Sets the optional coverage database to use
     *
     * @param PhingFile The database file
     */
    public function setDatabase(PhingFile $database)
    {
        $this->_database = $database;
    }

    /**
     * Create classpath object
     *
     * @return Path
     */
    public function createClasspath()
    {
        $this->_classpath = new Path();

        return $this->_classpath;
    }

    /**
     * Sets the coverage threshold for entire project
     *
     * @param integer $threshold Coverage threshold for entire project
     */
    public function setPerProject($threshold)
    {
        $this->_perProject = $threshold;
    }

    /**
     * Sets the coverage threshold for any class
     *
     * @param integer $threshold Coverage threshold for any class
     */
    public function setPerClass($threshold)
    {
        $this->_perClass = $threshold;
    }

    /**
     * Sets the coverage threshold for any method
     *
     * @param integer $threshold Coverage threshold for any method
     */
    public function setPerMethod($threshold)
    {
        $this->_perMethod = $threshold;
    }

    /**
     * Sets whether to enable detailed logging or not
     *
     * @param boolean $verbose
     */
    public function setVerbose($verbose)
    {
        $this->_verbose = StringHelper::booleanValue($verbose);
    }

    /**
     * Filter covered statements
     *
     * @param  integer $var Coverage CODE/count
     * @return boolean
     */
    protected function filterCovered($var)
    {
        return ($var >= 0 || $var === -2);
    }

    /**
     * Create excludes object
     *
     * @return Excludes
     */
    public function createExcludes()
    {
        $this->_excludes = new Excludes($this->project);

        return $this->_excludes;
    }

    /**
     * Calculates the coverage threshold
     *
     * @param string $filename The filename to analyse
     * @param array $coverageInformation Array with coverage information
     * @throws BuildException
     */
    protected function calculateCoverageThreshold($filename, $coverageInformation)
    {
        $classes = PHPUnitUtil::getDefinedClasses($filename, $this->_classpath);

        if (is_array($classes)) {
            foreach ($classes as $className) {
                // Skip class if excluded from coverage threshold validation
                if ($this->_excludes !== null) {
                    if (in_array($className, $this->_excludes->getExcludedClasses())) {
                        continue;
                    }
                }

                $reflection = new ReflectionClass($className);
                $classStartLine = $reflection->getStartLine();

                // Strange PHP5 reflection bug, classes without parent class
                // or implemented interfaces seem to start one line off
                if ($reflection->getParentClass() === null
                    && count($reflection->getInterfaces()) === 0
                ) {
                    unset($coverageInformation[$classStartLine + 1]);
                } else {
                    unset($coverageInformation[$classStartLine]);
                }

                reset($coverageInformation);

                $methods = $reflection->getMethods();

                foreach ($methods as $method) {
                    // PHP5 reflection considers methods of a parent class
                    // to be part of a subclass, we don't
                    if ($method->getDeclaringClass()->getName() != $reflection->getName()) {
                        continue;
                    }

                    // Skip method if excluded from coverage threshold validation
                    if ($this->_excludes !== null) {
                        $excludedMethods = $this->_excludes->getExcludedMethods();

                        if (isset($excludedMethods[$className])) {
                            if (in_array($method->getName(), $excludedMethods[$className])
                                || in_array($method->getName() . '()', $excludedMethods[$className])
                            ) {
                                continue;
                            }
                        }
                    }

                    $methodStartLine = $method->getStartLine();
                    $methodEndLine = $method->getEndLine();

                    // small fix for XDEBUG_CC_UNUSED
                    if (isset($coverageInformation[$methodStartLine])) {
                        unset($coverageInformation[$methodStartLine]);
                    }

                    if (isset($coverageInformation[$methodEndLine])) {
                        unset($coverageInformation[$methodEndLine]);
                    }

                    if ($method->isAbstract()) {
                        continue;
                    }

                    $lineNr = key($coverageInformation);

                    while ($lineNr !== null && $lineNr < $methodStartLine) {
                        next($coverageInformation);
                        $lineNr = key($coverageInformation);
                    }

                    $methodStatementsCovered = 0;
                    $methodStatementCount = 0;

                    while ($lineNr !== null && $lineNr <= $methodEndLine) {
                        $methodStatementCount++;

                        $lineCoverageInfo = $coverageInformation[$lineNr];
                        // set covered when CODE is other than -1 (not executed)
                        if ($lineCoverageInfo > 0 || $lineCoverageInfo === -2) {
                            $methodStatementsCovered++;
                        }

                        next($coverageInformation);
                        $lineNr = key($coverageInformation);
                    }

                    if ($methodStatementCount > 0) {
                        $methodCoverage = ($methodStatementsCovered
                                / $methodStatementCount) * 100;
                    } else {
                        $methodCoverage = 0;
                    }

                    if ($methodCoverage < $this->_perMethod
                        && !$method->isAbstract()
                    ) {
                        throw new BuildException(
                            'The coverage (' . round($methodCoverage, 2) . '%) '
                            . 'for method "' . $method->getName() . '" is lower'
                            . ' than the specified threshold ('
                            . $this->_perMethod . '%), see file: "'
                            . $filename . '"'
                        );
                    } elseif ($methodCoverage < $this->_perMethod
                        && $method->isAbstract()
                        && $this->_verbose === true
                    ) {
                        $this->log(
                            'Skipped coverage threshold for abstract method "'
                            . $method->getName() . '"'
                        );
                    }

                    // store the minimum coverage value for logging (see #466)
                    if ($this->_minMethodCoverageFound !== null) {
                        if ($this->_minMethodCoverageFound > $methodCoverage) {
                            $this->_minMethodCoverageFound = $methodCoverage;
                        }
                    } else {
                        $this->_minMethodCoverageFound = $methodCoverage;
                    }
                }

                $classStatementCount = count($coverageInformation);
                $classStatementsCovered = count(
                    array_filter(
                        $coverageInformation,
                        array($this, 'filterCovered')
                    )
                );

                if ($classStatementCount > 0) {
                    $classCoverage = ($classStatementsCovered
                            / $classStatementCount) * 100;
                } else {
                    $classCoverage = 0;
                }

                if ($classCoverage < $this->_perClass
                    && !$reflection->isAbstract()
                ) {
                    throw new BuildException(
                        'The coverage (' . round($classCoverage, 2) . '%) for class "'
                        . $reflection->getName() . '" is lower than the '
                        . 'specified threshold (' . $this->_perClass . '%), '
                        . 'see file: "' . $filename . '"'
                    );
                } elseif ($classCoverage < $this->_perClass
                    && $reflection->isAbstract()
                    && $this->_verbose === true
                ) {
                    $this->log(
                        'Skipped coverage threshold for abstract class "'
                        . $reflection->getName() . '"'
                    );
                }

                // store the minimum coverage value for logging (see #466)
                if ($this->_minClassCoverageFound !== null) {
                    if ($this->_minClassCoverageFound > $classCoverage) {
                        $this->_minClassCoverageFound = $classCoverage;
                    }
                } else {
                    $this->_minClassCoverageFound = $classCoverage;
                }

                $this->_projectStatementCount += $classStatementCount;
                $this->_projectStatementsCovered += $classStatementsCovered;
            }
        }
    }

    public function main()
    {
        if ($this->_database === null) {
            $coverageDatabase = $this->project
                ->getProperty('coverage.database');

            if (!$coverageDatabase) {
                throw new BuildException(
                    'Either include coverage-setup in your build file or set '
                    . 'the "database" attribute'
                );
            }

            $database = new PhingFile($coverageDatabase);
        } else {
            $database = $this->_database;
        }

        $this->log(
            'Calculating coverage threshold: min. '
            . $this->_perProject . '% per project, '
            . $this->_perClass . '% per class and '
            . $this->_perMethod . '% per method is required'
        );

        $props = new Properties();
        $props->load($database);

        foreach ($props->keys() as $filename) {
            $file = unserialize($props->getProperty($filename));

            // Skip file if excluded from coverage threshold validation
            if ($this->_excludes !== null) {
                if (in_array($file['fullname'], $this->_excludes->getExcludedFiles())) {
                    continue;
                }
            }

            $this->calculateCoverageThreshold(
                $file['fullname'],
                $file['coverage']
            );
        }

        if ($this->_projectStatementCount > 0) {
            $coverage = ($this->_projectStatementsCovered
                    / $this->_projectStatementCount) * 100;
        } else {
            $coverage = 0;
        }

        if ($coverage < $this->_perProject) {
            throw new BuildException(
                'The coverage (' . round($coverage, 2) . '%) for the entire project '
                . 'is lower than the specified threshold ('
                . $this->_perProject . '%)'
            );
        }

        $this->log(
            'Passed coverage threshold. Minimum found coverage values are: '
            . round($coverage, 2) . '% per project, '
            . round($this->_minClassCoverageFound, 2) . '% per class and '
            . round($this->_minMethodCoverageFound, 2) . '% per method'
        );
    }
}
<?php
/*
 *  $Id: b0540d6f531fdc2a5103a09c92a54318ebdf8c0b $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/creole/CreoleTask.php';
include_once 'phing/system/io/StringReader.php';

/**
 * Executes a series of SQL statements on a database using Creole.
 *
 * <p>Statements can
 * either be read in from a text file using the <i>src</i> attribute or from
 * between the enclosing SQL tags.</p>
 *
 * <p>Multiple statements can be provided, separated by semicolons (or the
 * defined <i>delimiter</i>). Individual lines within the statements can be
 * commented using either --, // or REM at the start of the line.</p>
 *
 * <p>The <i>autocommit</i> attribute specifies whether auto-commit should be
 * turned on or off whilst executing the statements. If auto-commit is turned
 * on each statement will be executed and committed. If it is turned off the
 * statements will all be executed as one transaction.</p>
 *
 * <p>The <i>onerror</i> attribute specifies how to proceed when an error occurs
 * during the execution of one of the statements.
 * The possible values are: <b>continue</b> execution, only show the error;
 * <b>stop</b> execution and commit transaction;
 * and <b>abort</b> execution and transaction and fail task.</p>
 *
 * @author    Hans Lellelid <hans@xmpl.org> (Phing)
 * @author    Jeff Martin <jeff@custommonkey.org> (Ant)
 * @author    Michael McCallum <gholam@xtra.co.nz> (Ant)
 * @author    Tim Stephenson <tim.stephenson@sybase.com> (Ant)
 * @package   phing.tasks.ext.creole
 * @version   $Id: b0540d6f531fdc2a5103a09c92a54318ebdf8c0b $
 */
class CreoleSQLExecTask extends CreoleTask
{

    private $goodSql = 0;
    private $totalSql = 0;

    const DELIM_ROW = "row";
    const DELIM_NORMAL = "normal";

    /**
     * Database connection
     */
    private $conn = null;

    /**
     * files to load
     */
    private $filesets = array();

    /**
     * all filterchains objects assigned to this task
     */
    private $filterChains = array();

    /**
     * SQL statement
     */
    private $statement = null;

    /**
     * SQL input file
     */
    private $srcFile = null;

    /**
     * SQL input command
     */
    private $sqlCommand = "";

    /**
     * SQL transactions to perform
     */
    private $transactions = array();

    /**
     * SQL Statement delimiter
     */
    private $delimiter = ";";

    /**
     * The delimiter type indicating whether the delimiter will
     * only be recognized on a line by itself
     */
    private $delimiterType = "normal"; // can't use constant just defined

    /**
     * Print SQL results.
     */
    private $print = false;

    /**
     * Print header columns.
     */
    private $showheaders = true;

    /**
     * Results Output file.
     */
    private $output = null;


    /**
     * Action to perform if an error is found
     **/
    private $onError = "abort";

    /**
     * Encoding to use when reading SQL statements from a file
     */
    private $encoding = null;

    /**
     * Append to an existing file or overwrite it?
     */
    private $append = false;

    /**
     * Set the name of the SQL file to be run.
     * Required unless statements are enclosed in the build file
     * @param PhingFile $srcFile
     */
    public function setSrc(PhingFile $srcFile)
    {
        $this->srcFile = $srcFile;
    }

    /**
     * Set an inline SQL command to execute.
     * NB: Properties are not expanded in this text.
     * @param $sql
     */
    public function addText($sql)
    {
        $this->sqlCommand .= $sql;
    }

    /**
     * Adds a set of files (nested fileset attribute).
     * @param FileSet $set
     */
    public function addFileset(FileSet $set)
    {
        $this->filesets[] = $set;
    }

    /**
     * Creates a filterchain
     *
     * @return object The created filterchain object
     */
    public function createFilterChain()
    {
        $num = array_push($this->filterChains, new FilterChain($this->project));

        return $this->filterChains[$num - 1];
    }

    /**
     * Add a SQL transaction to execute
     */
    public function createTransaction()
    {
        $t = new SQLExecTransaction($this);
        $this->transactions[] = $t;

        return $t;
    }

    /**
     * Set the file encoding to use on the SQL files read in
     *
     * @param the $encoding
     * @internal param the $encoding encoding to use on the files
     */
    public function setEncoding($encoding)
    {
        $this->encoding = $encoding;
    }

    /**
     * Set the statement delimiter.
     *
     * <p>For example, set this to "go" and delimitertype to "ROW" for
     * Sybase ASE or MS SQL Server.</p>
     *
     * @param delimiter
     */
    public function setDelimiter($delimiter)
    {
        $this->delimiter = $delimiter;
    }

    /**
     * Set the Delimiter type for this sql task. The delimiter type takes two
     * values - normal and row. Normal means that any occurrence of the delimiter
     * terminate the SQL command whereas with row, only a line containing just
     * the delimiter is recognized as the end of the command.
     *
     * @param string $delimiterType
     */
    public function setDelimiterType($delimiterType)
    {
        $this->delimiterType = $delimiterType;
    }

    /**
     * Set the print flag.
     *
     * @param boolean $print
     */
    public function setPrint($print)
    {
        $this->print = (boolean) $print;
    }

    /**
     * Print headers for result sets from the
     * statements; optional, default true.
     * @param boolean $showheaders
     */
    public function setShowheaders($showheaders)
    {
        $this->showheaders = (boolean) $showheaders;
    }

    /**
     * Set the output file;
     * optional, defaults to the console.
     * @param PhingFile $output
     */
    public function setOutput(PhingFile $output)
    {
        $this->output = $output;
    }

    /**
     * whether output should be appended to or overwrite
     * an existing file.  Defaults to false.
     * @param $append
     */
    public function setAppend($append)
    {
        $this->append = (boolean) $append;
    }


    /**
     * Action to perform when statement fails: continue, stop, or abort
     * optional; default &quot;abort&quot;
     * @param $action
     */
    public function setOnerror($action)
    {
        $this->onError = $action;
    }

    /**
     * Load the sql file and then execute it
     * @throws BuildException
     */
    public function main()
    {

        $savedTransaction = array();
        for ($i = 0, $size = count($this->transactions); $i < $size; $i++) {
            $savedTransaction[] = clone $this->transactions[$i];
        }

        $savedSqlCommand = $this->sqlCommand;

        $this->sqlCommand = trim($this->sqlCommand);

        try {
            if ($this->srcFile === null && $this->sqlCommand === ""
                && empty($this->filesets)
            ) {
                if (count($this->transactions) === 0) {
                    throw new BuildException("Source file or fileset, "
                        . "transactions or sql statement "
                        . "must be set!", $this->location);
                }
            }

            if ($this->srcFile !== null && !$this->srcFile->exists()) {
                throw new BuildException("Source file does not exist!", $this->location);
            }

            // deal with the filesets
            for ($i = 0, $size = count($this->filesets); $i < $size; $i++) {
                $fs = $this->filesets[$i];
                $ds = $fs->getDirectoryScanner($this->project);
                $srcDir = $fs->getDir($this->project);

                $srcFiles = $ds->getIncludedFiles();

                // Make a transaction for each file
                for ($j = 0, $size = count($srcFiles); $j < $size; $j++) {
                    $t = $this->createTransaction();
                    $t->setSrc(new PhingFile($srcDir, $srcFiles[$j]));
                }
            }

            // Make a transaction group for the outer command
            $t = $this->createTransaction();
            if ($this->srcFile) {
                $t->setSrc($this->srcFile);
            }
            $t->addText($this->sqlCommand);
            $this->conn = $this->getConnection();

            try {

                $this->statement = $this->conn->createStatement();

                $out = null;

                try {

                    if ($this->output !== null) {
                        $this->log("Opening output file " . $this->output, Project::MSG_VERBOSE);
                        $out = new BufferedWriter(new FileWriter($this->output->getAbsolutePath(), $this->append));
                    }

                    // Process all transactions
                    for ($i = 0, $size = count($this->transactions); $i < $size; $i++) {
                        $this->transactions[$i]->runTransaction($out);
                        if (!$this->isAutocommit()) {
                            $this->log("Committing transaction", Project::MSG_VERBOSE);
                            $this->conn->commit();
                        }
                    }
                    if ($out) {
                        $out->close();
                    }
                } catch (Exception $e) {
                    if ($out) {
                        $out->close();
                    }
                    throw $e;
                }
            } catch (IOException $e) {
                if (!$this->isAutocommit() && $this->conn !== null && $this->onError == "abort") {
                    try {
                        $this->conn->rollback();
                    } catch (SQLException $ex) {
                    }
                }
                throw new BuildException($e->getMessage(), $this->location);
            } catch (SQLException $e) {
                if (!$this->isAutocommit() && $this->conn !== null && $this->onError == "abort") {
                    try {
                        $this->conn->rollback();
                    } catch (SQLException $ex) {
                    }
                }
                throw new BuildException($e->getMessage(), $this->location);
            }

            $this->log(
                $this->goodSql . " of " . $this->totalSql .
                " SQL statements executed successfully"
            );
        } catch (Exception $e) {
            $this->transactions = $savedTransaction;
            $this->sqlCommand = $savedSqlCommand;
            throw $e;
        }
        // finally {
        $this->transactions = $savedTransaction;
        $this->sqlCommand = $savedSqlCommand;

    }


    /**
     * read in lines and execute them
     * @param Reader $reader
     * @param null $out
     * @throws BuildException
     */
    public function runStatements(Reader $reader, $out = null)
    {
        $sql = "";
        $line = "";

        $buffer = '';

        if ((is_array($this->filterChains)) && (!empty($this->filterChains))) {
            $in = FileUtils::getChainedReader(new BufferedReader($reader), $this->filterChains, $this->getProject());
            while (-1 !== ($read = $in->read())) { // -1 indicates EOF
                $buffer .= $read;
            }
            $lines = explode("\n", $buffer);
        } else {
            $in = new BufferedReader($reader);

            while (($line = $in->readLine()) !== null) {
                $lines[] = $line;
            }
        }

        try {
            foreach ($lines as $line) {
                $line = trim($line);
                $line = ProjectConfigurator::replaceProperties(
                    $this->project,
                    $line,
                    $this->project->getProperties()
                );

                if (StringHelper::startsWith("//", $line) ||
                    StringHelper::startsWith("--", $line) ||
                    StringHelper::startsWith("#", $line)
                ) {
                    continue;
                }

                if (strlen($line) > 4
                    && strtoupper(substr($line, 0, 4)) == "REM "
                ) {
                    continue;
                }

                $sql .= " " . $line;
                $sql = trim($sql);

                // SQL defines "--" as a comment to EOL
                // and in Oracle it may contain a hint
                // so we cannot just remove it, instead we must end it
                if (strpos($line, "--") !== false) {
                    $sql .= "\n";
                }

                if ($this->delimiterType == self::DELIM_NORMAL
                    && StringHelper::endsWith($this->delimiter, $sql)
                    || $this->delimiterType == self::DELIM_ROW
                    && $line == $this->delimiter
                ) {
                    $this->log("SQL: " . $sql, Project::MSG_VERBOSE);
                    $this->execSQL(StringHelper::substring($sql, 0, strlen($sql) - strlen($this->delimiter)), $out);
                    $sql = "";
                }
            }

            // Catch any statements not followed by ;
            if ($sql !== "") {
                $this->execSQL($sql, $out);
            }
        } catch (SQLException $e) {
            throw new BuildException("Error running statements", $e);
        }
    }

    /**
     * Exec the sql statement.
     * @param $sql
     * @param null $out
     * @throws BuildException
     */
    protected function execSQL($sql, $out = null)
    {
        // Check and ignore empty statements
        if (trim($sql) == "") {
            return;
        }

        try {
            $this->totalSql++;
            if (!$this->statement->execute($sql)) {
                $this->log($this->statement->getUpdateCount() . " rows affected", Project::MSG_VERBOSE);
            } else {
                if ($this->print) {
                    $this->printResults($out);
                }
            }

            $this->goodSql++;

        } catch (SQLException $e) {
            $this->log("Failed to execute: " . $sql, Project::MSG_ERR);
            if ($this->onError != "continue") {
                throw new BuildException("Failed to execute SQL", $e);
            }
            $this->log($e->getMessage(), Project::MSG_ERR);
        }
    }

    /**
     * print any results in the statement.
     * @param null $out
     */
    protected function printResults($out = null)
    {

        $rs = null;
        do {
            $rs = $this->statement->getResultSet();

            if ($rs !== null) {

                $this->log("Processing new result set.", Project::MSG_VERBOSE);

                $line = "";

                $colsprinted = false;

                while ($rs->next()) {
                    $fields = $rs->getRow();

                    if (!$colsprinted && $this->showheaders) {
                        $first = true;
                        foreach ($fields as $fieldName => $ignore) {
                            if ($first) {
                                $first = false;
                            } else {
                                $line .= ",";
                            }
                            $line .= $fieldName;
                        }
                        if ($out !== null) {
                            $out->write($line);
                            $out->newLine();
                        } else {
                            print($line . PHP_EOL);
                        }
                        $line = "";
                        $colsprinted = true;
                    } // if show headers

                    $first = true;
                    foreach ($fields as $columnValue) {

                        if ($columnValue != null) {
                            $columnValue = trim($columnValue);
                        }

                        if ($first) {
                            $first = false;
                        } else {
                            $line .= ",";
                        }
                        $line .= $columnValue;
                    }

                    if ($out !== null) {
                        $out->write($line);
                        $out->newLine();
                    } else {
                        print($line . PHP_EOL);
                    }
                    $line = "";

                } // while rs->next()
            }
        } while ($this->statement->getMoreResults());
        print(PHP_EOL);
        if ($out !== null) {
            $out->newLine();
        }
    }
}

/**
 * "Inner" class that contains the definition of a new transaction element.
 * Transactions allow several files or blocks of statements
 * to be executed using the same JDBC connection and commit
 * operation in between.
 *
 * @package   phing.tasks.ext.creole
 */
class SQLExecTransaction
{

    private $tSrcFile = null;
    private $tSqlCommand = "";
    private $parent;

    /**
     * @param $parent
     */
    public function __construct($parent)
    {
        // Parent is required so that we can log things ...
        $this->parent = $parent;
    }

    /**
     * @param PhingFile $src
     */
    public function setSrc(PhingFile $src)
    {
        $this->tSrcFile = $src;
    }

    /**
     * @param $sql
     */
    public function addText($sql)
    {
        $this->tSqlCommand .= $sql;
    }

    /**
     * @param null $out
     */
    public function runTransaction($out = null)
    {
        if (!empty($this->tSqlCommand)) {
            $this->parent->log("Executing commands", Project::MSG_INFO);
            $this->parent->runStatements(new StringReader($this->tSqlCommand), $out);
        }

        if ($this->tSrcFile !== null) {
            $this->parent->log(
                "Executing file: " . $this->tSrcFile->getAbsolutePath(),
                Project::MSG_INFO
            );

            $reader = new FileReader($this->tSrcFile);

            $this->parent->runStatements($reader, $out);
            $reader->close();
        }
    }
}
<?php

/*
 *  $Id: b020d667f8bb1d09d43607ffaa4ff6e33bd91dbd $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/types/Reference.php';

/**
 * Handles Creole configuration needed by SQL type tasks.
 *
 * @author    Hans Lellelid <hans@xmpl.org> (Phing)
 * @author    Nick Chalko <nick@chalko.com> (Ant)
 * @author    Jeff Martin <jeff@custommonkey.org> (Ant)
 * @author    Michael McCallum <gholam@xtra.co.nz> (Ant)
 * @author    Tim Stephenson <tim.stephenson@sybase.com> (Ant)
 * @version   $Id: b020d667f8bb1d09d43607ffaa4ff6e33bd91dbd $
 * @package   phing.tasks.system
 */
abstract class CreoleTask extends Task
{

    /**
     * Used for caching loaders / driver. This is to avoid
     * getting an OutOfMemoryError when calling this task
     * multiple times in a row.
     *
     * NOT IMPLEMENTED YET
     */
    private static $loaderMap = array();

    private $caching = true;

    /**
     * Autocommit flag. Default value is false
     */
    private $autocommit = false;

    /**
     * [optional] Classpath to Creole driver to use.
     * @param string
     */
    private $driver;

    /**
     * DB url.
     */
    private $url;

    /**
     * User name.
     */
    private $userId;

    /**
     * Password
     */
    private $password;

    /**
     * RDBMS Product needed for this SQL.
     **/
    private $rdbms;

    /**
     * Initialize CreoleTask.
     * This method includes any necessary Creole libraries and triggers
     * appropriate error if they cannot be found.  This is not done in header
     * because we may want this class to be loaded w/o triggering an error.
     */
    public function init()
    {
        include_once 'creole/Creole.php';
        if (!class_exists('Creole')) {
            throw new Exception("Creole task depends on Creole classes being on include_path. (i.e. include of 'creole/Creole.php' failed.)");
        }
    }

    /**
     * Caching loaders / driver. This is to avoid
     * getting an OutOfMemoryError when calling this task
     * multiple times in a row; default: true
     * @param bool $enable
     */
    public function setCaching($enable)
    {
        $this->caching = $enable;
    }

    /**
     * Sets the database connection URL; required.
     * @param string $url
     */
    public function setUrl($url)
    {
        $this->url = $url;
    }

    /**
     * Set the Creole driver to be used.
     *
     * @param string $driver driver class name
     */
    public function setDriver($driver)
    {
        $this->driver = $driver;
    }

    /**
     * Sets the password; required.
     * @param string $password The password to set
     */
    public function setPassword($password)
    {
        $this->password = $password;
    }

    /**
     * Auto commit flag for database connection;
     * optional, default false.
     *
     * @param bool $autocommit The autocommit to set
     */
    public function setAutocommit($autocommit)
    {
        $this->autocommit = $autocommit;
    }

    /**
     * Sets the version string, execute task only if
     * rdbms version match; optional.
     *
     * @param string $version
     */
    public function setVersion($version)
    {
        $this->version = $version;
    }

    /**
     * @return array
     */
    protected function getLoaderMap()
    {
        return self::$loaderMap;
    }

    /**
     * Creates a new Connection as using the driver, url, userid and password specified.
     * The calling method is responsible for closing the connection.
     *
     * @return Connection     the newly created connection.
     *
     * @throws BuildException if the UserId/Password/Url is not set or there is no suitable driver or the driver fails to load.
     */
    protected function getConnection()
    {

        if ($this->url === null) {
            throw new BuildException("Url attribute must be set!", $this->location);
        }

        try {

            $this->log("Connecting to " . $this->getUrl(), Project::MSG_VERBOSE);
            $info = new Properties();

            $dsn = Creole::parseDSN($this->url);

            if (!isset($dsn["username"]) && $this->userId === null) {
                throw new BuildException("Username must be in URL or userid attribute must be set.", $this->location);
            }

            if ($this->userId) {
                $dsn["username"] = $this->getUserId();
            }

            if ($this->password) {
                $dsn["password"] = $this->getPassword();
            }

            if ($this->driver) {
                Creole::registerDriver($dsn['phptype'], $this->driver);
            }

            $conn = Creole::getConnection($dsn);
            $conn->setAutoCommit($this->autocommit);

            return $conn;

        } catch (SQLException $e) {
            throw new BuildException($e->getMessage(), $this->location);
        }

    }

    /**
     * @param $value
     */
    public function isCaching($value)
    {
        $this->caching = $value;
    }

    /**
     * Gets the autocommit.
     * @return Returns a boolean
     */
    public function isAutocommit()
    {
        return $this->autocommit;
    }

    /**
     * Gets the url.
     *
     * @return string
     */
    public function getUrl()
    {
        return $this->url;
    }

    /**
     * Gets the userId.
     *
     * @return string
     */
    public function getUserId()
    {
        return $this->userId;
    }

    /**
     * Set the user name for the connection; required.
     * @param string $userId
     */
    public function setUserid($userId)
    {
        $this->userId = $userId;
    }

    /**
     * Gets the password.
     * @return string
     */
    public function getPassword()
    {
        return $this->password;
    }
}
<?php
/*
 *  $Id: 4a3c05035ee7d8e1b9c019e218eaa84cf6979df8 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/dbdeploy/DbmsSyntaxFactory.php';

/**
 * Generate SQL script for db using dbdeploy schema version table
 * and delta scripts
 *
 * <dbdeploy url="mysql:host=localhost;dbname=test"
 *     userid="dbdeploy" password="dbdeploy" dir="db" outputfile="">
 *
 * @author   Luke Crouch at SourceForge (http://sourceforge.net)
 * @version  $Id: 4a3c05035ee7d8e1b9c019e218eaa84cf6979df8 $
 * @package  phing.tasks.ext.dbdeploy
 */
class DbDeployTask extends Task
{
    /**
     * The tablename to use from the database for storing all changes
     * This cannot be changed
     *
     * @var string
     */
    public static $TABLE_NAME = 'changelog';

    /**
     * Connection string for the database connection
     *
     * @var string
     */
    protected $url;

    /**
     * The userid for the database connection
     *
     * @var string
     */
    protected $userid;

    /**
     * The password of the database user
     *
     * @var string
     */
    protected $password;

    /**
     * Path to the directory that holds the database patch files
     *
     * @var string
     */
    protected $dir;

    /**
     * Output file for performing all database patches of this deployment
     * Contains all the SQL statements that need to be executed
     *
     * @var string
     */
    protected $outputFile = 'dbdeploy_deploy.sql';

    /**
     * Outputfile for undoing the database patches of this deployment
     * Contains all the SQL statements that need to be executed
     *
     * @var string
     */
    protected $undoOutputFile = 'dbdeploy_undo.sql';

    /**
     * The deltaset that's being used
     *
     * @var string
     */
    protected $deltaSet = 'Main';

    /**
     * The number of the last change to apply
     *
     * @var int
     */
    protected $lastChangeToApply = 999;

    /**
     * Contains the object for the DBMS that is used
     *
     * @var object
     */
    protected $dbmsSyntax = null;

    /**
     * Array with all change numbers that are applied already
     *
     * @var array
     */
    protected $appliedChangeNumbers = array();

    /**
     * Checkall attribute
     * False means dbdeploy will only apply patches that have a higher number
     * than the last patchnumber that was applied
     * True means dbdeploy will apply all changes that aren't applied
     * already (in ascending order)
     *
     * @var int
     */
    protected $checkall = false;

    /**
     * The value of the 'applied_by' column for
     * each changelog entry
     *
     * @var string
     */
    protected $appliedBy = 'dbdeploy';

    /**
     * The main function for the task
     *
     * @throws BuildException
     * @return void
     */
    public function main()
    {
        try {
            // get correct DbmsSyntax object
            $dbms = substr($this->url, 0, strpos($this->url, ':'));
            $dbmsSyntaxFactory = new DbmsSyntaxFactory($dbms);
            $this->dbmsSyntax = $dbmsSyntaxFactory->getDbmsSyntax();

            // figure out which revisions are in the db already
            $this->appliedChangeNumbers = $this->getAppliedChangeNumbers();
            $this->log('Current db revision: ' . $this->getLastChangeAppliedInDb());
            $this->log('Checkall: ' . ($this->checkall ? 'On' : 'Off'));

            $this->deploy();

        } catch (Exception $e) {
            throw new BuildException($e);
        }
    }

    /**
     * Get the numbers of all the patches that are already applied according to
     * the changelog table in the database
     *
     * @return array
     */
    protected function getAppliedChangeNumbers()
    {
        if (count($this->appliedChangeNumbers) == 0) {
            $this->log('Getting applied changed numbers from DB: ' . $this->url);
            $appliedChangeNumbers = array();
            $dbh = new PDO($this->url, $this->userid, $this->password);
            $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->dbmsSyntax->applyAttributes($dbh);
            $sql = "SELECT *
                    FROM " . DbDeployTask::$TABLE_NAME . "
                    WHERE delta_set = '$this->deltaSet'
                    ORDER BY change_number";
            foreach ($dbh->query($sql) as $change) {
                $appliedChangeNumbers[] = $change['change_number'];
            }
            $this->appliedChangeNumbers = $appliedChangeNumbers;
        }

        return $this->appliedChangeNumbers;
    }

    /**
     * Get the number of the last patch applied to the database
     *
     * @return int|mixed The highest patch number that is applied in the db
     */
    protected function getLastChangeAppliedInDb()
    {
        return (count($this->appliedChangeNumbers) > 0)
            ? max($this->appliedChangeNumbers) : 0;
    }

    /**
     * Create the deploy and undo deploy outputfiles
     *
     * @return void
     */
    protected function deploy()
    {
        // create deploy outputfile
        $this->createOutputFile($this->outputFile, false);

        // create undo deploy outputfile
        $this->createOutputFile($this->undoOutputFile, true);
    }

    /**
     * Generate the sql for doing/undoing the deployment and write it to a file
     *
     * @param  string $file
     * @param  bool   $undo
     * @return void
     */
    protected function createOutputFile($file, $undo = false)
    {
        $fileHandle = fopen($file, "w+");
        $sql = $this->generateSql($undo);
        fwrite($fileHandle, $sql);
    }

    /**
     * Generate the sql for doing/undoing this deployment
     *
     * @param  bool   $undo
     * @return string The sql
     */
    protected function generateSql($undo = false)
    {
        $sql = '';
        $lastChangeAppliedInDb = $this->getLastChangeAppliedInDb();
        $files = $this->getDeltasFilesArray();
        $this->sortFiles($files, $undo);

        foreach ($files as $fileChangeNumber => $fileName) {
            if ($this->fileNeedsToBeRead($fileChangeNumber, $lastChangeAppliedInDb)) {
                $sql .= '-- Fragment begins: ' . $fileChangeNumber . ' --' . "\n";

                if (!$undo) {
                    $sql .= 'INSERT INTO ' . DbDeployTask::$TABLE_NAME . '
                                (change_number, delta_set, start_dt, applied_by, description)' .
                        ' VALUES (' . $fileChangeNumber . ', \'' . $this->deltaSet . '\', ' .
                        $this->dbmsSyntax->generateTimestamp() .
                        ', \'' . $this->appliedBy . '\', \'' . $fileName . '\');' . "\n";
                }

                // read the file
                $fullFileName = $this->dir . '/' . $fileName;
                $fh = fopen($fullFileName, 'r');
                $contents = fread($fh, filesize($fullFileName));
                $count_bad_comments = substr_count($contents, '--//');
                if ($count_bad_comments > 0) {
                    $this->log('Your SQL delta includes "--//" which, if a comment, should be replaced with "-- //"
                    to avoid the delta failing.  You may need to manually undo part of this delta.\n\n'
                        . $contents, Project::MSG_WARN);
                }
                // allow construct with and without space added
                $split = strpos($contents, '-- //@UNDO');
                if ($split === false) {
                    $split = strpos($contents, '--//@UNDO');
                }
                if ($split === false) {
                    $split = strlen($contents);
                }

                if ($undo) {
                    $sql .= substr($contents, $split + 10) . "\n";
                    $sql .= 'DELETE FROM ' . DbDeployTask::$TABLE_NAME . '
	                         WHERE change_number = ' . $fileChangeNumber . '
	                         AND delta_set = \'' . $this->deltaSet . '\';' . "\n";
                } else {
                    $sql .= substr($contents, 0, $split);
                    // Ensuring there's a newline after the final -- //
                    $sql .= PHP_EOL;
                    $sql .= 'UPDATE ' . DbDeployTask::$TABLE_NAME . '
	                         SET complete_dt = ' . $this->dbmsSyntax->generateTimestamp() . '
	                         WHERE change_number = ' . $fileChangeNumber . '
	                         AND delta_set = \'' . $this->deltaSet . '\';' . "\n";
                }

                $sql .= '-- Fragment ends: ' . $fileChangeNumber . ' --' . "\n";
            }
        }

        return $sql;
    }

    /**
     * Get a list of all the patch files in the patch file directory
     *
     * @return array
     */
    protected function getDeltasFilesArray()
    {
        $files = array();

        $baseDir = realpath($this->dir);
        $dh = opendir($baseDir);

        if ($dh === false) {
            return $files;
        }

        $fileChangeNumberPrefix = '';
        while (($file = readdir($dh)) !== false) {
            if (preg_match('[\d+]', $file, $fileChangeNumberPrefix)) {
                $files[intval($fileChangeNumberPrefix[0])] = $file;
            }
        }

        return $files;
    }

    /**
     * Sort files in the patch files directory (ascending or descending depending on $undo boolean)
     *
     * @param  array $files
     * @param  bool  $undo
     * @return void
     */
    protected function sortFiles(&$files, $undo)
    {
        if ($undo) {
            krsort($files);
        } else {
            ksort($files);
        }
    }

    /**
     * Determine if this patch file need to be deployed
     * (using fileChangeNumber, lastChangeAppliedInDb and $this->checkall)
     *
     * @param  int    $fileChangeNumber
     * @param  string $lastChangeAppliedInDb
     * @return bool   True or false if patch file needs to be deployed
     */
    protected function fileNeedsToBeRead($fileChangeNumber, $lastChangeAppliedInDb)
    {
        if ($this->checkall) {
            return (!in_array($fileChangeNumber, $this->appliedChangeNumbers));
        } else {
            return ($fileChangeNumber > $lastChangeAppliedInDb && $fileChangeNumber <= $this->lastChangeToApply);
        }
    }

    /**
     * Set the url for the database connection
     *
     * @param  string $url
     * @return void
     */
    public function setUrl($url)
    {
        $this->url = $url;
    }

    /**
     * Set the userid for the database connection
     *
     * @param  string $userid
     * @return void
     */
    public function setUserId($userid)
    {
        $this->userid = $userid;
    }

    /**
     * Set the password for the database connection
     *
     * @param  string $password
     * @return void
     */
    public function setPassword($password)
    {
        $this->password = $password;
    }

    /**
     * Set the directory where to find the patchfiles
     *
     * @param  string $dir
     * @return void
     */
    public function setDir($dir)
    {
        $this->dir = $dir;
    }

    /**
     * Set the outputfile which contains all patch sql statements for this deployment
     *
     * @param  string $outputFile
     * @return void
     */
    public function setOutputFile($outputFile)
    {
        $this->outputFile = $outputFile;
    }

    /**
     * Set the undo outputfile which contains all undo statements for this deployment
     *
     * @param  string $undoOutputFile
     * @return void
     */
    public function setUndoOutputFile($undoOutputFile)
    {
        $this->undoOutputFile = $undoOutputFile;
    }

    /**
     * Set the lastchangetoapply property
     *
     * @param  int  $lastChangeToApply
     * @return void
     */
    public function setLastChangeToApply($lastChangeToApply)
    {
        $this->lastChangeToApply = $lastChangeToApply;
    }

    /**
     * Set the deltaset property
     *
     * @param  string $deltaSet
     * @return void
     */
    public function setDeltaSet($deltaSet)
    {
        $this->deltaSet = $deltaSet;
    }

    /**
     * Set the checkall property
     *
     * @param  bool $checkall
     * @return void
     */
    public function setCheckAll($checkall)
    {
        $this->checkall = (int) $checkall;
    }

    /**
     * Set the appliedBy property
     *
     * @param  string $appliedBy
     * @return void
     */
    public function setAppliedBy($appliedBy)
    {
        $this->appliedBy = $appliedBy;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }
}
<?php
/*
 *  $Id: e8f266768b58a63d2f0a4a40e1c4c220b81901ec $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Utility class for generating necessary server-specific SQL commands
 *
 * @author   Luke Crouch at SourceForge (http://sourceforge.net)
 * @version  $Id: e8f266768b58a63d2f0a4a40e1c4c220b81901ec $
 * @package  phing.tasks.ext.dbdeploy
 */
abstract class DbmsSyntax
{
    /**
     * @param $db
     */
    public function applyAttributes($db)
    {
    }

    abstract public function generateTimestamp();
}
<?php
/*
 *  $Id: 1d817a30c44e1f87a0f51ecebdc9fefe3177790c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/dbdeploy/DbmsSyntax.php';

/**
 * Factory for generating dbms-specific syntax-generating objects
 *
 * @author   Luke Crouch at SourceForge (http://sourceforge.net)
 * @version  $Id: 1d817a30c44e1f87a0f51ecebdc9fefe3177790c $
 * @package  phing.tasks.ext.dbdeploy
 */
class DbmsSyntaxFactory
{
    private $dbms;

    /**
     * @param $dbms
     */
    public function __construct($dbms)
    {
        $this->dbms = $dbms;
    }

    public function getDbmsSyntax()
    {
        switch ($this->dbms) {
            case('sqlite') :
                require_once 'phing/tasks/ext/dbdeploy/DbmsSyntaxSQLite.php';

                return new DbmsSyntaxSQLite();
            case('mysql'):
                require_once 'phing/tasks/ext/dbdeploy/DbmsSyntaxMysql.php';

                return new DbmsSyntaxMysql();
            case 'odbc':
            case('mssql'):
            case 'dblib':
                require_once 'phing/tasks/ext/dbdeploy/DbmsSyntaxMsSql.php';

                return new DbmsSyntaxMsSql();
            case('pgsql'):
                require_once 'phing/tasks/ext/dbdeploy/DbmsSyntaxPgSQL.php';

                return new DbmsSyntaxPgSQL();
            case 'oci':
                require_once 'phing/tasks/ext/dbdeploy/DbmsSyntaxOracle.php';

                return new DbmsSyntaxOracle();
            default:
                throw new Exception($this->dbms . ' is not supported by dbdeploy task.');
        }
    }
}
<?php
/*
 *  $Id: ff91eb484802be6835d70ee11c6c9d7fed1e09df $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Utility class for generating necessary server-specific SQL commands
 *
 * @author   Luke Crouch at SourceForge (http://sourceforge.net)
 * @version  $Id: ff91eb484802be6835d70ee11c6c9d7fed1e09df $
 * @package  phing.tasks.ext.dbdeploy
 */
class DbmsSyntaxMsSql extends DbmsSyntax
{
    /**
     * @return string
     */
    public function generateTimestamp()
    {
        return "DATEDIFF(s, '19700101', GETDATE())";
    }
}
<?php
/*
 *  $Id: 2df6bddd9e2070c0cbc8d8eab2ee0a3caafb9b20 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Utility class for generating necessary server-specific SQL commands
 *
 * @author   Luke Crouch at SourceForge (http://sourceforge.net)
 * @version  $Id: 2df6bddd9e2070c0cbc8d8eab2ee0a3caafb9b20 $
 * @package  phing.tasks.ext.dbdeploy
 */
class DbmsSyntaxMysql extends DbmsSyntax
{
    /**
     * @return string
     */
    public function generateTimestamp()
    {
        return "NOW()";
    }
}
<?php
/*
 *  $Id: adc9b31780df2145519b4d451cb462527bd200f5 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Utility class for generating necessary server-specific SQL commands
 *
 * @author   Luke Crouch at SourceForge (http://sourceforge.net)
 * @version  $Id: adc9b31780df2145519b4d451cb462527bd200f5 $
 * @package  phing.tasks.ext.dbdeploy
 */
class DbmsSyntaxOracle extends DbmsSyntax
{
    /**
     * @param $db
     */
    public function applyAttributes($db)
    {
        $db->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
    }

    /**
     * @return string
     */
    public function generateTimestamp()
    {
        return "(sysdate - to_date('01-JAN-1970','DD-MON-YYYY')) * (86400)";
    }
}
<?php
/*
 *  $Id: ee3d162c42e59a87e2cde17d64360fa16955fa85 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Utility class for generating necessary server-specific SQL commands
 *
 * @author   Remy BREUILS
 * @version  $Id: ee3d162c42e59a87e2cde17d64360fa16955fa85 $
 * @package  phing.tasks.ext.dbdeploy
 */
class DbmsSyntaxPgSQL extends DbmsSyntax
{
    /**
     * @return string
     */
    public function generateTimestamp()
    {
        return "NOW()";
    }
}
<?php
/*
 *  $Id: f154ae7623dcdc04e60284fe70bea1da99ab1254 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Utility class for generating necessary server-specific SQL commands
 *
 * @author   Luke Crouch at SourceForge (http://sourceforge.net)
 * @version  $Id: f154ae7623dcdc04e60284fe70bea1da99ab1254 $
 * @package  phing.tasks.ext.dbdeploy
 */
class DbmsSyntaxSQLite extends DbmsSyntax
{
    /**
     * @return string
     */
    public function generateTimestamp()
    {
        return "strftime('%s','now')";
    }
}
<?php

/*
 *  $Id: 6c960e196084540721a620b8fe1c97c642f69d40 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once "phing/Task.php";

/**
 * Saves currently defined properties into a specified file
 *
 * @author Andrei Serdeliuc
 * @extends Task
 * @version   $Id: 6c960e196084540721a620b8fe1c97c642f69d40 $
 * @package   phing.tasks.ext
 */
class ExportPropertiesTask extends Task
{
    /**
     * Array of project properties
     *
     * (default value: null)
     *
     * @var array
     */
    private $_properties = null;

    /**
     * Target file for saved properties
     *
     * (default value: null)
     *
     * @var string
     */
    private $_targetFile = null;

    /**
     * Exclude properties starting with these prefixes
     *
     * @var array
     */
    private $_disallowedPropertyPrefixes = array(
        'host.',
        'phing.',
        'os.',
        'php.',
        'line.',
        'env.',
        'user.'
    );

    /**
     * setter for _targetFile
     *
     * @param  string $file
     * @throws BuildException
     * @return bool
     */
    public function setTargetFile($file)
    {
        if (!is_dir(dirname($file))) {
            throw new BuildException("Parent directory of target file doesn't exist");
        }

        if (!is_writable(dirname($file)) && (file_exists($file) && !is_writable($file))) {
            throw new BuildException("Target file isn't writable");
        }

        $this->_targetFile = $file;

        return true;
    }

    /**
     * setter for _disallowedPropertyPrefixes
     *
     * @param $prefixes
     * @internal param string $file
     * @return bool
     */
    public function setDisallowedPropertyPrefixes($prefixes)
    {
        $this->_disallowedPropertyPrefixes = explode(",", $prefixes);

        return true;
    }

    public function main()
    {
        // Sets the currently declared properties
        $this->_properties = $this->getProject()->getProperties();

        if (is_array($this->_properties) && !empty($this->_properties) && null !== $this->_targetFile) {
            $propertiesString = '';
            foreach ($this->_properties as $propertyName => $propertyValue) {
                if (!$this->isDisallowedPropery($propertyName)) {
                    $propertiesString .= $propertyName . "=" . $propertyValue . PHP_EOL;
                }
            }

            if (!file_put_contents($this->_targetFile, $propertiesString)) {
                throw new BuildException('Failed writing to ' . $this->_targetFile);
            }
        }
    }

    /**
     * Checks if a property name is disallowed
     *
     * @param  string $propertyName
     * @return bool
     */
    protected function isDisallowedPropery($propertyName)
    {
        foreach ($this->_disallowedPropertyPrefixes as $property) {
            if (substr($propertyName, 0, strlen($property)) == $property) {
                return true;
            }
        }

        return false;
    }
}
<?php
/*
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/MatchingTask.php';

/**
 * Base class for extracting tasks such as Unzip and Untar.
 *
 * @author    Joakim Bodin <joakim.bodin+phing@gmail.com>
 * @version   $Id: 9a02922d4ca2d2a31655411257558307260482b5 $
 * @package   phing.tasks.ext
 * @since     2.2.0
 */
abstract class ExtractBaseTask extends MatchingTask
{
    /**
     * @var PhingFile $file
     */
    protected $file;
    /**
     * @var PhingFile $todir
     */
    protected $todir;
    protected $removepath;
    protected $filesets = array(); // all fileset objects assigned to this task

    /**
     * Set to true to always extract (and possibly overwrite)
     * all files from the archive
     * @var boolean
     */
    protected $forceExtract = false;

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Set the name of the zip file to extract.
     * @param PhingFile $file zip file to extract
     * @return void
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * This is the base directory to look in for things to zip.
     * @param PhingFile $todir
     * @return void
     */
    public function setToDir(PhingFile $todir)
    {
        $this->todir = $todir;
    }

    /**
     * @param $removepath
     * @return void
     */
    public function setRemovePath($removepath)
    {
        $this->removepath = $removepath;
    }

    /**
     * Sets the forceExtract attribute
     * @param boolean $forceExtract
     * @return void
     */
    public function setForceExtract($forceExtract)
    {
        $this->forceExtract = (bool) $forceExtract;
    }

    /**
     * do the work
     * @throws BuildException
     */
    public function main()
    {

        $this->validateAttributes();

        $filesToExtract = array();
        if ($this->file !== null) {
            if ($this->forceExtract || !$this->isDestinationUpToDate($this->file)) {
                $filesToExtract[] = $this->file;
            } else {
                $this->log(
                    'Nothing to do: ' . $this->todir->getAbsolutePath(
                    ) . ' is up to date for ' . $this->file->getCanonicalPath(),
                    Project::MSG_INFO
                );
            }
        }

        foreach ($this->filesets as $compressedArchiveFileset) {
            $compressedArchiveDirScanner = $compressedArchiveFileset->getDirectoryScanner($this->project);
            $compressedArchiveFiles = $compressedArchiveDirScanner->getIncludedFiles();
            $compressedArchiveDir = $compressedArchiveFileset->getDir($this->project);

            foreach ($compressedArchiveFiles as $compressedArchiveFilePath) {
                $compressedArchiveFile = new PhingFile($compressedArchiveDir, $compressedArchiveFilePath);
                if ($compressedArchiveFile->isDirectory()) {
                    throw new BuildException($compressedArchiveFile->getAbsolutePath(
                        ) . ' compressed archive cannot be a directory.');
                }

                if ($this->forceExtract || !$this->isDestinationUpToDate($compressedArchiveFile)) {
                    $filesToExtract[] = $compressedArchiveFile;
                } else {
                    $this->log(
                        'Nothing to do: ' . $this->todir->getAbsolutePath(
                        ) . ' is up to date for ' . $compressedArchiveFile->getCanonicalPath(),
                        Project::MSG_INFO
                    );
                }
            }
        }

        foreach ($filesToExtract as $compressedArchiveFile) {
            $this->extractArchive($compressedArchiveFile);
        }
    }

    /**
     * @param PhingFile $compressedArchiveFile
     * @return mixed
     */
    abstract protected function extractArchive(PhingFile $compressedArchiveFile);

    /**
     * @param PhingFile $compressedArchiveFile
     * @throws BuildException
     * @internal param array $files array of filenames
     * @internal param PhingFile $dir
     * @return boolean
     */
    protected function isDestinationUpToDate(PhingFile $compressedArchiveFile)
    {
        if (!$compressedArchiveFile->exists()) {
            throw new BuildException("Could not find file " . $compressedArchiveFile->__toString() . " to extract.");
        }

        $compressedArchiveContent = $this->listArchiveContent($compressedArchiveFile);
        if (is_array($compressedArchiveContent)) {

            $fileSystem = FileSystem::getFileSystem();
            foreach ($compressedArchiveContent as $compressArchivePathInfo) {
                $compressArchiveFilename = $compressArchivePathInfo['filename'];
                if (!empty($this->removepath) && strlen($compressArchiveFilename) >= strlen($this->removepath)) {
                    $compressArchiveFilename = preg_replace(
                        '/^' . $this->removepath . '/',
                        '',
                        $compressArchiveFilename
                    );
                }
                $compressArchivePath = new PhingFile($this->todir, $compressArchiveFilename);

                if (!$compressArchivePath->exists() ||
                    $fileSystem->compareMTimes(
                        $compressedArchiveFile->getCanonicalPath(),
                        $compressArchivePath->getCanonicalPath()
                    ) == 1
                ) {
                    return false;
                }
            }

        }

        return true;
    }

    /**
     * @param PhingFile $compressedArchiveFile
     * @return mixed
     */
    abstract protected function listArchiveContent(PhingFile $compressedArchiveFile);

    /**
     * Validates attributes coming in from XML
     *
     * @return void
     * @throws BuildException
     */
    protected function validateAttributes()
    {

        if ($this->file === null && count($this->filesets) === 0) {
            throw new BuildException("Specify at least one source compressed archive - a file or a fileset.");
        }

        if ($this->todir === null) {
            throw new BuildException("todir must be set.");
        }

        if ($this->todir !== null && $this->todir->exists() && !$this->todir->isDirectory()) {
            throw new BuildException("todir must be a directory.");
        }

        if ($this->file !== null && $this->file->exists() && $this->file->isDirectory()) {
            throw new BuildException("Compressed archive file cannot be a directory.");
        }

        if ($this->file !== null && !$this->file->exists()) {
            throw new BuildException("Could not find compressed archive file " . $this->file->__toString(
                ) . " to extract.");
        }
    }

}
<?php
/*
 * $Id: 9557fd39412c948a2387145de7876edd0392ca2e $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */
require_once 'phing/Task.php';

/**
 * fileHash
 *
 * Calculate either MD5 or SHA hash value of a specified file and retun the
 * value in a property
 *
 * @author      Johan Persson <johan162@gmail.com>
 * @version     $Id: 9557fd39412c948a2387145de7876edd0392ca2e $
 * @package     phing.tasks.ext
 */
class FileHashTask extends Task
{
    /**
     * Property for File
     * @var PhingFile file
     */
    private $file;

    /**
     * Property to be set
     * @var string $property
     */
    private $propertyName = "filehashvalue";

    /**
     * Specify which hash algorithm to use.
     *   0 = MD5
     *   1 = SHA1
     *
     * @var integer $hashtype
     */
    private $hashtype = 0;

    private $algorithm = '';

    /**
     * Specify if MD5 or SHA1 hash should be used
     * @param integer $type 0=MD5, 1=SHA1
     */
    public function setHashtype($type)
    {
        $this->hashtype = $type;
    }

    public function setAlgorithm($type)
    {
        $this->algorithm = strtolower($type);
    }

    /**
     * Which file to calculate the hash value of
     * @param PhingFile $file
     */
    public function setFile($file)
    {
        $this->file = $file;
    }

    /**
     * Set the name of the property to store the hash value in
     * @param $property
     * @return void
     */
    public function setPropertyName($property)
    {
        $this->propertyName = $property;
    }

    /**
     * Main-Method for the Task
     *
     * @return void
     * @throws BuildException
     */
    public function main()
    {
        $this->checkFile();
        $this->checkPropertyName();

        // read file
        if ($this->algorithm !== '' && in_array($this->algorithm, hash_algos())) {
            $this->log("Calculating $this->algorithm hash from: " . $this->file);
            $hashValue = hash_file($this->algorithm, $this->file);
        } elseif ((int) $this->hashtype === 0) {
            $this->log("Calculating MD5 hash from: " . $this->file);
            $hashValue = md5_file($this->file, false);
        } elseif ((int) $this->hashtype === 1) {
            $this->log("Calculating SHA1 hash from: " . $this->file);
            $hashValue = sha1_file($this->file, false);
        } else {
            if ($this->algorithm !== '') {
                throw new BuildException(
                    sprintf(
                        '[FileHash] Unknown algorithm specified %d. Must be one of %s',
                        $this->algorithm,
                        implode(', ', hash_algos())
                    )
                );
            } else {
                throw new BuildException(
                    sprintf(
                        '[FileHash] Unknown hashtype specified %d. Must be either 0 (=MD5) or 1 (=SHA1)',
                        $this->hashtype
                    ));
            }
        }

        // publish hash value
        $this->project->setProperty($this->propertyName, $hashValue);
    }

    /**
     * checks file attribute
     * @return void
     * @throws BuildException
     */
    private function checkFile()
    {
        // check File
        if ($this->file === null ||
            strlen($this->file) == 0
        ) {
            throw new BuildException('[FileHash] You must specify an input file.', $this->file);
        }

        if (!is_readable($this->file)) {
            throw new BuildException(
                sprintf(
                    '[FileHash] Input file does not exist or is not readable: %s',
                    $this->file
                )
            );
        }
    }

    /**
     * checks property attribute
     * @return void
     * @throws BuildException
     */
    private function checkPropertyName()
    {
        if (is_null($this->propertyName) ||
            strlen($this->propertyName) === 0
        ) {
            throw new BuildException('Property name for publishing hashvalue is not set');
        }
    }
}
<?php
/*
 * $Id: d4b97a2e512ffe10394e203d6a94c498e11ca986 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */
require_once 'phing/Task.php';

/**
 * fileHash
 *
 * Calculate either MD5 or SHA hash value of a specified file and retun the
 * value in a property
 *
 * @author      Johan Persson <johan162@gmail.com>
 * @version     $Id: d4b97a2e512ffe10394e203d6a94c498e11ca986 $
 * @package     phing.tasks.ext
 */
class FileSizeTask extends Task
{
    /**
     * Property for File
     * @var PhingFile file
     */
    private $file;

    /**
     * Property where the file size will be stored
     * @var string $property
     */
    private $propertyName = "filesize";

    /**
     * Which file to calculate the file size of
     * @param PhingFile $file
     */
    public function setFile($file)
    {
        $this->file = $file;
    }

    /**
     * Set the name of the property to store the file size
     * @param $property
     * @return void
     */
    public function setPropertyName($property)
    {
        $this->propertyName = $property;
    }

    /**
     * Main-Method for the Task
     *
     * @return void
     * @throws BuildException
     */
    public function main()
    {
        $this->checkFile();
        $this->checkPropertyName();

        $size = filesize($this->file);

        if ($size === false) {
            throw new BuildException(sprintf('[FileSize] Cannot determine size of file: %s', $this->file));

        }

        // publish hash value
        $this->project->setProperty($this->propertyName, $size);

    }

    /**
     * checks file attribute
     * @return void
     * @throws BuildException
     */
    private function checkFile()
    {
        // check File
        if ($this->file === null ||
            strlen($this->file) == 0
        ) {
            throw new BuildException('[FileSize] You must specify an input file.', $this->file);
        }

        if (!is_readable($this->file)) {
            throw new BuildException(sprintf(
                '[FileSize] Input file does not exist or is not readable: %s',
                $this->file
            ));
        }

    }

    /**
     * checks property attribute
     * @return void
     * @throws BuildException
     */
    private function checkPropertyName()
    {
        if (is_null($this->propertyName) ||
            strlen($this->propertyName) === 0
        ) {
            throw new BuildException('[FileSize] Property name for publishing file size is not set');
        }
    }
}
<?php
/*
 * $Id: dbcc96ca45a476e2beb94ff215b9d52eb115dbbc $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once "phing/Task.php";

/**
 * The FileSyncTask class copies files either to or from a remote host, or locally
 * on the current host. It allows rsync to transfer the differences between two
 * sets of files across the network connection, using an efficient checksum-search
 * algorithm.
 *
 * There are 4 different ways of using FileSyncTask:
 *
 *   1. For copying local files.
 *   2. For copying from the local machine to a remote machine using a remote
 *      shell program as the transport (ssh).
 *   3. For copying from a remote machine to the local machine using a remote
 *      shell program.
 *   4. For listing files on a remote machine.
 *
 * This is extended from Federico's original code, all his docs are kept in here below.
 *
 * @author    Federico Cargnelutti <fede.carg@gmail.com>
 * @author    Anton Stöckl <anton@stoeckl.de>
 * @version   $Revision$
 * @package   phing.tasks.ext
 * @see       http://svn.fedecarg.com/repo/Phing/tasks/ext/FileSyncTask.php
 * @example   http://fedecarg.com/wiki/FileSyncTask
 */
class FileSyncTask extends Task
{
    /**
     * Path to rsync command.
     * @var string
     */
    protected $rsyncPath = '/usr/bin/rsync';

    /**
     * Source directory.
     * For remote sources this must contain user and host, e.g.: user@host:/my/source/dir
     * @var string
     */
    protected $sourceDir;

    /**
     * Destination directory.
     * For remote targets this must contain user and host, e.g.: user@host:/my/target/dir
     * @var string
     */
    protected $destinationDir;

    /**
     * Remote host.
     * @var string
     */
    protected $remoteHost;

    /**
     * Rsync auth username.
     * @var string
     */
    protected $remoteUser;

    /**
     * Rsync auth password.
     * @var string
     */
    protected $remotePass;

    /**
     * Remote shell.
     * @var string
     */
    protected $remoteShell;

    /**
     * Exclude file matching pattern.
     * Use comma seperated values to exclude multiple files/directories, e.g.: a,b
     * @var string
     */
    protected $exclude;

    /**
     * Excluded patterns file.
     * @var string
     */
    protected $excludeFile;

    /**
     * This option creates a backup so users can rollback to an existing restore
     * point. The remote directory is copied to a new directory specified by the
     * user.
     * @var string
     */
    protected $backupDir;

    /**
     * Default command options.
     * r - recursive
     * p - preserve permissions
     * K - treat symlinked dir on receiver as dir
     * z - compress
     * l - copy symlinks as symlinks
     * @var string
     */
    protected $defaultOptions = '-rpKzl';

    /**
     * Command options.
     * @var string
     */
    protected $options;

    /**
     * Connection type.
     * @var boolean
     */
    protected $isRemoteConnection = false;

    /**
     * This option increases the amount of information you are given during the
     * transfer. The verbose option set to true will give you information about
     * what files are being transferred and a brief summary at the end.
     * @var boolean
     */
    protected $verbose = true;

    /**
     * This option makes rsync perform a trial run that doesn’t make any changes
     * (and produces mostly the same output as a real run).
     * @var boolean
     */
    protected $dryRun = false;

    /**
     * This option makes requests a simple itemized list of the changes that are
     * being made to each file, including attribute changes.
     * @var boolean
     */
    protected $itemizeChanges = false;

    /**
     * This option will cause rsync to skip files based on checksum, not mod-time & size.
     * @var boolean
     */
    protected $checksum = false;

    /**
     * This option deletes files that don't exist on sender.
     * @var boolean
     */
    protected $delete = false;

    /**
     * Identity file.
     * @var string
     */
    protected $identityFile;

    /**
     * Phing's main method. Wraps the executeCommand() method.
     *
     * @return void
     */
    public function main()
    {
        $this->executeCommand();
    }

    /**
     * Executes the rsync command and returns the exit code.
     *
     * @return int            Return code from execution.
     * @throws BuildException
     */
    public function executeCommand()
    {
        if ($this->rsyncPath === null) {
            throw new BuildException('The "rsyncPath" attribute is missing or undefined.');
        }

        if ($this->sourceDir === null) {
            throw new BuildException('The "sourcedir" attribute is missing or undefined.');
        } else {
            if ($this->destinationDir === null) {
                throw new BuildException('The "destinationdir" attribute is missing or undefined.');
            }
        }

        if (strpos($this->destinationDir, ':')) {
            $this->setIsRemoteConnection(true);
        }

        if (strpos($this->sourceDir, ':')) {
            if ($this->isRemoteConnection) {
                throw new BuildException('The source and destination cannot both be remote.');
            }
            $this->setIsRemoteConnection(true);
        } else {
            if (!(is_dir($this->sourceDir) && is_readable($this->sourceDir))) {
                throw new BuildException('No such file or directory: ' . $this->sourceDir);
            }
        }

        if ($this->backupDir !== null && $this->backupDir == $this->destinationDir) {
            throw new BuildException("Invalid backup directory: " . $this->backupDir);
        }

        $command = $this->getCommand();

        $output = array();
        $return = null;
        exec($command, $output, $return);

        $lines = '';
        foreach ($output as $line) {
            if (!empty($line)) {
                $lines .= "\r\n" . "\t\t\t" . $line;
            }
        }

        $this->log($command);

        if ($return != 0) {
            $this->log('Task exited with code: ' . $return, Project::MSG_ERR);
            $this->log(
                'Task exited with message: (' . $return . ') ' . $this->getErrorMessage($return),
                Project::MSG_ERR
            );
            throw new BuildException($return . ': ' . $this->getErrorMessage($return));
        } else {
            $this->log($lines, Project::MSG_INFO);
        }

        return $return;
    }

    /**
     * Returns the rsync command line options.
     *
     * @return string
     */
    public function getCommand()
    {
        $options = $this->defaultOptions;

        if ($this->options !== null) {
            $options = $this->options;
        }

        if ($this->verbose === true) {
            $options .= ' --verbose';
        }

        if ($this->checksum === true) {
            $options .= ' --checksum';
        }

        if ($this->identityFile !== null) {
            $options .= ' -e "ssh -i ' . $this->identityFile . '"';
        } else {
            if ($this->remoteShell !== null) {
                $options .= ' -e "' . $this->remoteShell . '"';
            }
        }

        if ($this->dryRun === true) {
            $options .= ' --dry-run';
        }

        if ($this->delete === true) {
            $options .= ' --delete-after --ignore-errors --force';
        }

        if ($this->itemizeChanges === true) {
            $options .= ' --itemize-changes';
        }
        if ($this->backupDir !== null) {
            $options .= ' -b --backup-dir="' . $this->backupDir . '"';
        }

        if ($this->exclude !== null) {
            //remove trailing comma if any
            $this->exclude = trim($this->exclude, ',');
            $options .= ' --exclude="' . str_replace(',', '" --exclude="', $this->exclude) . '"';
        }

        if ($this->excludeFile !== null) {
            $options .= ' --exclude-from="' . $this->excludeFile . '"';
        }

        $this->setOptions($options);

        $options .= ' "' . $this->sourceDir . '" "' . $this->destinationDir . '"';

        escapeshellcmd($options);
        $options .= ' 2>&1';

        return $this->rsyncPath . ' ' . $options;
    }

    /**
     * Returns an error message based on a given error code.
     *
     * @param  int         $code Error code
     * @return null|string
     */
    public function getErrorMessage($code)
    {
        $error[0] = 'Success';
        $error[1] = 'Syntax or usage error';
        $error[2] = 'Protocol incompatibility';
        $error[3] = 'Errors selecting input/output files, dirs';
        $error[4] = 'Requested action not supported: an attempt was made to manipulate '
            . '64-bit files on a platform that cannot support them; or an option '
            . 'was specified that is supported by the client and not by the server';
        $error[5] = 'Error starting client-server protocol';
        $error[10] = 'Error in socket I/O';
        $error[11] = 'Error in file I/O';
        $error[12] = 'Error in rsync protocol data stream';
        $error[13] = 'Errors with program diagnostics';
        $error[14] = 'Error in IPC code';
        $error[20] = 'Received SIGUSR1 or SIGINT';
        $error[21] = 'Some error returned by waitpid()';
        $error[22] = 'Error allocating core memory buffers';
        $error[23] = 'Partial transfer due to error';
        $error[24] = 'Partial transfer due to vanished source files';
        $error[30] = 'Timeout in data send/receive';

        if (array_key_exists($code, $error)) {
            return $error[$code];
        }

        return null;
    }

    /**
     * Sets the path to the rsync command.
     *
     * @param  string $path
     * @return void
     */
    public function setRsyncPath($path)
    {
        $this->rsyncPath = $path;
    }

    /**
     * Sets the isRemoteConnection property.
     *
     * @param  boolean $isRemote
     * @return void
     */
    protected function setIsRemoteConnection($isRemote)
    {
        $this->isRemoteConnection = $isRemote;
    }

    /**
     * Sets the source directory.
     *
     * @param  string $dir
     * @return void
     */
    public function setSourceDir($dir)
    {
        $this->sourceDir = $dir;
    }

    /**
     * Sets the command options.
     *
     * @param  string $options
     * @return void
     */
    public function setOptions($options)
    {
        $this->options = $options;
    }

    /**
     * Sets the destination directory. If the option remotehost is not included
     * in the build.xml file, rsync will point to a local directory instead.
     *
     * @param  string $dir
     * @return void
     */
    public function setDestinationDir($dir)
    {
        $this->destinationDir = $dir;
    }

    /**
     * Sets the remote host.
     *
     * @param  string $host
     * @return void
     */
    public function setRemoteHost($host)
    {
        $this->remoteHost = $host;
    }

    /**
     * Specifies the user to log in as on the remote machine. This also may be
     * specified in the properties file.
     *
     * @param  string $user
     * @return void
     */
    public function setRemoteUser($user)
    {
        $this->remoteUser = $user;
    }

    /**
     * This option allows you to provide a password for accessing a remote rsync
     * daemon. Note that this option is only useful when accessing an rsync daemon
     * using the built in transport, not when using a remote shell as the transport.
     *
     * @param  string $pass
     * @return void
     */
    public function setRemotePass($pass)
    {
        $this->remotePass = $pass;
    }

    /**
     * Allows the user to choose an alternative remote shell program to use for
     * communication between the local and remote copies of rsync. Typically,
     * rsync is configured to use ssh by default, but you may prefer to use rsh
     * on a local network.
     *
     * @param  string $shell
     * @return void
     */
    public function setRemoteShell($shell)
    {
        $this->remoteShell = $shell;
    }

    /**
     * Increases the amount of information you are given during the
     * transfer. By default, rsync works silently. A single -v will give you
     * information about what files are being transferred and a brief summary at
     * the end.
     *
     * @param  boolean $verbose
     * @return void
     */
    public function setVerbose($verbose)
    {
        $this->verbose = (bool) $verbose;
    }

    /**
     * This changes the way rsync checks if the files have been changed and are in need of a transfer.
     * Without this option, rsync  uses  a "quick  check"  that  (by  default)  checks if each file’s
     * size and time of last modification match between the sender and receiver.
     * This option changes this to compare a 128-bit checksum for each file that has a matching size.
     *
     * @param  boolean $checksum
     * @return void
     */
    public function setChecksum($checksum)
    {
        $this->checksum = (bool) $checksum;
    }

    /**
     * This makes rsync perform a trial run that doesn’t make any changes (and produces mostly the same
     * output as a real run).  It is  most commonly used in combination with the -v, --verbose and/or
     * -i, --itemize-changes options to see what an rsync command is going to do before one actually runs it.
     *
     * @param  boolean $dryRun
     * @return void
     */
    public function setDryRun($dryRun)
    {
        $this->dryRun = (bool) $dryRun;
    }

    /**
     * Requests a simple itemized list of the changes that are being made to each file, including attribute changes.
     *
     * @param  boolean $itemizeChanges
     * @return void
     */
    public function setItemizeChanges($itemizeChanges)
    {
        $this->itemizeChanges = (bool) $itemizeChanges;
    }

    /**
     * Tells rsync to delete extraneous files from the receiving side, but only
     * for the directories that are being synchronized. Files that are excluded
     * from transfer are also excluded from being deleted.
     *
     * @param  boolean $delete
     * @return void
     */
    public function setDelete($delete)
    {
        $this->delete = (bool) $delete;
    }

    /**
     * Exclude files matching patterns from $file, Blank lines in $file and
     * lines starting with ';' or '#' are ignored.
     *
     * @param  string $file
     * @return void
     */
    public function setExcludeFile($file)
    {
        $this->excludeFile = $file;
    }

    /**
     * Makes backups into hierarchy based in $dir.
     *
     * @param string dir
     * @return void
     */
    public function setBackupDir($dir)
    {
        $this->backupDir = $dir;
    }

    /**
     * Sets the identity file for public key transfers.
     *
     * @param string location of ssh identity file
     * @return void
     */
    public function setIdentityFile($identity)
    {
        $this->identityFile = $identity;
    }

    /**
     * Sets exclude matching pattern.
     *
     * @param string $exclude
     * @return void
     */
    public function setExclude($exclude)
    {
        $this->exclude = $exclude;
    }
}
<?php
/**
 * $Id: e52538418d5dac1ccf34f61e145a44b1fb18b34f $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * FtpDeployTask
 *
 * Deploys a set of files to a remote FTP server.
 *
 *
 * Example usage:
 * <ftpdeploy host="host" port="21" username="user" password="password" dir="public_html" mode="ascii" clearfirst="true" depends="false" filemode="" dirmode="">
 *   <fileset dir=".">
 *     <include name="**"/>
 *     <exclude name="phing"/>
 *     <exclude name="build.xml"/>
 *     <exclude name="images/**.png"/>
 *     <exclude name="images/**.gif"/>
 *     <exclude name="images/**.jpg"/>
 *   </fileset>
 * </ftpdeploy>
 *
 * @author Jorrit Schippers <jorrit at ncode dot nl>
 * @contributor Steffen Sørensen <steffen@sublife.dk>
 * @version $Id: e52538418d5dac1ccf34f61e145a44b1fb18b34f $
 * @since 2.3.1
 * @package  phing.tasks.ext
 */
class FtpDeployTask extends Task
{
    private $host = null;
    private $port = 21;
    private $ssl = false;
    private $username = null;
    private $password = null;
    private $dir = null;
    private $filesets;
    private $completeDirMap;
    private $mode = FTP_BINARY;
    private $clearFirst = false;
    private $passive = false;
    private $depends = false;
    private $dirmode = false;
    private $filemode = false;
    private $rawDataFallback = false;
    private $skipOnSameSize = false;

    protected $logLevel = Project::MSG_VERBOSE;

    /**
     *
     */
    public function __construct()
    {
        $this->filesets = array();
        $this->completeDirMap = array();
    }

    /**
     * @param $host
     */
    public function setHost($host)
    {
        $this->host = $host;
    }

    /**
     * @param $port
     */
    public function setPort($port)
    {
        $this->port = (int) $port;
    }

    /**
     * @param $ssl
     */
    public function setSsl($ssl)
    {
        $this->ssl = (bool) $ssl;
    }

    /**
     * @param $username
     */
    public function setUsername($username)
    {
        $this->username = $username;
    }

    /**
     * @param $password
     */
    public function setPassword($password)
    {
        $this->password = $password;
    }

    /**
     * @param $dir
     */
    public function setDir($dir)
    {
        $this->dir = $dir;
    }

    /**
     * @param $mode
     */
    public function setMode($mode)
    {
        switch (strtolower($mode)) {
            case 'ascii':
                $this->mode = FTP_ASCII;
                break;
            case 'binary':
            case 'bin':
                $this->mode = FTP_BINARY;
                break;
        }
    }

    /**
     * @param $passive
     */
    public function setPassive($passive)
    {
        $this->passive = (bool) $passive;
    }

    /**
     * @param $clearFirst
     */
    public function setClearFirst($clearFirst)
    {
        $this->clearFirst = (bool) $clearFirst;
    }

    /**
     * @param $depends
     */
    public function setDepends($depends)
    {
        $this->depends = (bool) $depends;
    }

    /**
     * @param $filemode
     */
    public function setFilemode($filemode)
    {
        $this->filemode = octdec(str_pad($filemode, 4, '0', STR_PAD_LEFT));
    }

    /**
     * @param $dirmode
     */
    public function setDirmode($dirmode)
    {
        $this->dirmode = octdec(str_pad($dirmode, 4, '0', STR_PAD_LEFT));
    }

    /**
     * @param $fallback
     */
    public function setRawdatafallback($fallback)
    {
        $this->rawDataFallback = (bool) $fallback;
    }

    /**
     * @param bool|string|int $skipOnSameSize
     */
    public function setSkipOnSameSize($skipOnSameSize)
    {
        $this->skipOnSameSize = StringHelper::booleanValue($skipOnSameSize);
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Set level of log messages generated (default = info)
     * @param string $level
     */
    public function setLevel($level)
    {
        switch ($level) {
            case "error":
                $this->logLevel = Project::MSG_ERR;
                break;
            case "warning":
                $this->logLevel = Project::MSG_WARN;
                break;
            case "info":
                $this->logLevel = Project::MSG_INFO;
                break;
            case "verbose":
                $this->logLevel = Project::MSG_VERBOSE;
                break;
            case "debug":
                $this->logLevel = Project::MSG_DEBUG;
                break;
        }
    }

    /**
     * The init method: check if Net_FTP is available
     */
    public function init()
    {
        $paths = Phing::explodeIncludePath();
        foreach ($paths as $path) {
            if (file_exists($path . DIRECTORY_SEPARATOR . 'Net' . DIRECTORY_SEPARATOR . 'FTP.php')) {
                return true;
            }
        }
        throw new BuildException('The FTP Deploy task requires the Net_FTP PEAR package.');
    }

    /**
     * The main entry point method.
     */
    public function main()
    {
        $project = $this->getProject();

        require_once 'PEAR.php';
        require_once 'Net/FTP.php';
        $ftp = new Net_FTP($this->host, $this->port);
        if ($this->ssl) {
            $ret = $ftp->setSsl();
            if (@PEAR::isError($ret)) {
                throw new BuildException('SSL connection not supported by php' . ': ' . $ret->getMessage(
                    ));
            } else {
                $this->log('Use SSL connection', $this->logLevel);
            }
        }
        $ret = $ftp->connect();
        if (@PEAR::isError($ret)) {
            throw new BuildException('Could not connect to FTP server ' . $this->host . ' on port ' . $this->port . ': ' . $ret->getMessage(
                ));
        } else {
            $this->log('Connected to FTP server ' . $this->host . ' on port ' . $this->port, $this->logLevel);
        }

        $ret = $ftp->login($this->username, $this->password);
        if (@PEAR::isError($ret)) {
            throw new BuildException('Could not login to FTP server ' . $this->host . ' on port ' . $this->port . ' with username ' . $this->username . ': ' . $ret->getMessage(
                ));
        } else {
            $this->log('Logged in to FTP server with username ' . $this->username, $this->logLevel);
        }

        if ($this->passive) {
            $this->log('Setting passive mode', $this->logLevel);
            $ret = $ftp->setPassive();
            if (@PEAR::isError($ret)) {
                $ftp->disconnect();
                throw new BuildException('Could not set PASSIVE mode: ' . $ret->getMessage());
            }
        }

        // append '/' to the end if necessary
        $dir = substr($this->dir, -1) == '/' ? $this->dir : $this->dir . '/';

        if ($this->clearFirst) {
            // TODO change to a loop through all files and directories within current directory
            $this->log('Clearing directory ' . $dir, $this->logLevel);
            $ftp->rm($dir, true);
        }

        // Create directory just in case
        $ret = $ftp->mkdir($dir, true);
        if (@PEAR::isError($ret)) {
            $ftp->disconnect();
            throw new BuildException('Could not create directory ' . $dir . ': ' . $ret->getMessage());
        }

        $ret = $ftp->cd($dir);
        if (@PEAR::isError($ret)) {
            $ftp->disconnect();
            throw new BuildException('Could not change to directory ' . $dir . ': ' . $ret->getMessage());
        } else {
            $this->log('Changed directory ' . $dir, $this->logLevel);
        }

        $fs = FileSystem::getFileSystem();
        $convert = $fs->getSeparator() == '\\';

        foreach ($this->filesets as $fs) {
            // Array for holding directory content informations
            $remoteFileInformations = array();

            $ds = $fs->getDirectoryScanner($project);
            $fromDir = $fs->getDir($project);
            $srcFiles = $ds->getIncludedFiles();
            $srcDirs = $ds->getIncludedDirectories();

            foreach ($srcDirs as $dirname) {
                if ($convert) {
                    $dirname = str_replace('\\', '/', $dirname);
                }

                // Read directory informations, if file exists, else create the directory
                if (!$this->_directoryInformations($ftp, $remoteFileInformations, $dirname)) {
                    $this->log('Will create directory ' . $dirname, $this->logLevel);
                    $ret = $ftp->mkdir($dirname, true);
                    if (@PEAR::isError($ret)) {
                        $ftp->disconnect();
                        throw new BuildException('Could not create directory ' . $dirname . ': ' . $ret->getMessage());
                    }
                }
                if ($this->dirmode) {
                    if ($this->dirmode == 'inherit') {
                        $mode = fileperms($dirname);
                    } else {
                        $mode = $this->dirmode;
                    }
                    // Because Net_FTP does not support a chmod call we call ftp_chmod directly
                    ftp_chmod($ftp->_handle, $mode, $dirname);
                }
            }

            foreach ($srcFiles as $filename) {
                $file = new PhingFile($fromDir->getAbsolutePath(), $filename);
                if ($convert) {
                    $filename = str_replace('\\', '/', $filename);
                }

                $local_filemtime = filemtime($file->getCanonicalPath());
                if (isset($remoteFileInformations[$filename]['stamp'])) {
                    $remoteFileModificationTime = $remoteFileInformations[$filename]['stamp'];
                } else {
                    $remoteFileModificationTime = 0;
                }

                if (!$this->depends || ($local_filemtime > $remoteFileModificationTime)) {

                    if ($this->skipOnSameSize === true && $file->length() === $ftp->size($filename)) {
                        $this->log('Skipped ' . $file->getCanonicalPath(), $this->logLevel);
                        continue;
                    }

                    $this->log('Will copy ' . $file->getCanonicalPath() . ' to ' . $filename, $this->logLevel);
                    $ret = $ftp->put($file->getCanonicalPath(), $filename, true, $this->mode);
                    if (@PEAR::isError($ret)) {
                        $ftp->disconnect();
                        throw new BuildException('Could not deploy file ' . $filename . ': ' . $ret->getMessage());
                    }
                }
                if ($this->filemode) {
                    if ($this->filemode == 'inherit') {
                        $mode = fileperms($filename);
                    } else {
                        $mode = $this->filemode;
                    }
                    // Because Net_FTP does not support a chmod call we call ftp_chmod directly
                    ftp_chmod($ftp->_handle, $mode, $filename);
                }
            }
        }

        $ftp->disconnect();
        $this->log('Disconnected from FTP server', $this->logLevel);
    }

    /**
     * @param Net_FTP $ftp
     * @param $remoteFileInformations
     * @param $directory
     * @return bool
     */
    private function _directoryInformations(Net_FTP $ftp, &$remoteFileInformations, $directory)
    {
        $content = $ftp->ls($directory);
        if (@PEAR::isError($content)) {
            if ($this->rawDataFallback) {
                $content = $ftp->ls($directory, NET_FTP_RAWLIST);
            }
            if (@PEAR::isError($content)) {
                return false;
            }
            $content = $this->parseRawFtpContent($content, $directory);
        }

        if (sizeof($content) == 0) {
            return false;
        } else {
            if (!empty($directory)) {
                $directory .= '/';
            }
            while (list(, $val) = each($content)) {
                if ($val['name'] != '.' && $val['name'] != '..') {
                    $remoteFileInformations[$directory . $val['name']] = $val;
                }
            }

            return true;
        }
    }

    /**
     * @param $content
     * @param null $directory
     * @return array
     */
    private function parseRawFtpContent($content, $directory = null)
    {
        if (!is_array($content)) {
            return array();
        }

        $this->log('Start parsing FTP_RAW_DATA in ' . $directory);
        $retval = array();
        foreach ($content as $rawInfo) {
            $rawInfo = explode(' ', $rawInfo);
            $rawInfo2 = array();
            foreach ($rawInfo as $part) {
                $part = trim($part);
                if (!empty($part)) {
                    $rawInfo2[] = $part;
                }
            }

            $date = date_parse($rawInfo2[5] . ' ' . $rawInfo2[6] . ' ' . $rawInfo2[7]);
            if ($date['year'] === false) {
                $date['year'] = date('Y');
            }
            $date = mktime(
                $date['hour'],
                $date['minute'],
                $date['second'],
                $date['month'],
                $date['day'],
                $date['year']
            );

            $retval[] = array(
                'name' => $rawInfo2[8],
                'rights' => substr($rawInfo2[0], 1),
                'user' => $rawInfo2[2],
                'group' => $rawInfo2[3],
                'date' => $date,
                'stamp' => $date,
                'is_dir' => strpos($rawInfo2[0], 'd') === 0,
                'files_inside' => (int) $rawInfo2[1],
                'size' => (int) $rawInfo2[4],
            );
        }

        $this->log('Finished parsing FTP_RAW_DATA in ' . $directory);

        return $retval;
    }

}
<?php
/*
 *  $Id: 85ccdb98750754b0376c5a367925b260788f4086 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/BuildException.php';

/**
 * Base class for Git tasks
 *
 * @author Victor Farazdagi <simple.square@gmail.com>
 * @version $Id: 85ccdb98750754b0376c5a367925b260788f4086 $
 * @package phing.tasks.ext.git
 * @see VersionControl_Git
 * @since 2.4.3
 */
abstract class GitBaseTask extends Task
{
    /**
     * Bath to git binary
     * @var string
     */
    private $gitPath = '/usr/bin/git';

    /**
     * @var VersionControl_Git
     */
    private $gitClient = null;

    /**
     * Current repository directory
     * @var string
     */
    private $repository;

    /**
     * Initialize Task.
     * Check and include necessary libraries.
     */
    public function init()
    {
        @include_once 'VersionControl/Git.php';
        if (false == class_exists('VersionControl_Git')) {
            throw new BuildException("The Git tasks depend on PEAR\'s "
                . "VersionControl_Git package.", $this->getLocation());
        }
    }

    /**
     * Set repository directory
     *
     * @param  string      $repository Repo directory
     * @return GitBaseTask
     */
    public function setRepository($repository)
    {
        $this->repository = $repository;

        return $this;
    }

    /**
     * Get repository directory
     *
     * @return string
     */
    public function getRepository()
    {
        return $this->repository;
    }

    /**
     * Set path to git executable
     *
     * @param  string      $gitPath New path to git repository
     * @return GitBaseTask
     */
    public function setGitPath($gitPath)
    {
        $this->gitPath = $gitPath;

        return $this;
    }

    /**
     * Get path to git executable
     *
     * @return string
     */
    public function getGitPath()
    {
        return $this->gitPath;
    }

    /**
     * @param bool $reset
     * @param null $repository
     * @return null|VersionControl_Git
     * @throws BuildException
     */
    protected function getGitClient($reset = false, $repository = null)
    {
        $this->gitClient = ($reset === true) ? null : $this->gitClient;
        $repository = (null === $repository)
            ? $this->getRepository()
            : $repository;

        if (null === $this->gitClient) {
            try {
                $this->gitClient = new VersionControl_Git($repository);
            } catch (VersionControl_Git_Exception $e) {
                // re-package
                throw new BuildException(
                    'You must specify readable directory as repository.', $e);

            }
        }
        $this->gitClient->setGitCommandPath($this->getGitPath());

        return $this->gitClient;
    }
}
<?php
/*
 *  $Id: 0a0ccdabd2fb96209a3f7c2b5069408502064e71 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/git/GitBaseTask.php';

/**
 * Wrapper aroung git-branch
 *
 * @author Victor Farazdagi <simple.square@gmail.com>
 * @version $Id: 0a0ccdabd2fb96209a3f7c2b5069408502064e71 $
 * @package phing.tasks.ext.git
 * @see VersionControl_Git
 * @since 2.4.3
 */
class GitBranchTask extends GitBaseTask
{
    /**
     * Branch name
     * @var string
     */
    private $branchname;

    /**
     * New Branch name for git-branch -m | -M
     * @var string
     */
    private $newbranch;

    /**
     * If not HEAD, specify starting point
     * @var string
     */
    private $startPoint;

    /**
     * --set-upstream key to git-branch
     * @var boolean
     */
    private $setUpstream = false;

    /**
     * --track key to git-branch
     * @var boolean
     */
    private $track = false;

    /**
     * --no-track key to git-branch
     * @var boolean
     */
    private $noTrack = false;

    /**
     * --force, -f key to git-branch
     * @var boolean
     */
    private $force = false;

    /**
     * -d, -D, -m, -M options to git-branch
     * Respective task options:
     * delete, forceDelete, move, forceMove
     * @var array
     */
    private $extraOptions = array(
        'd' => false,
        'D' => false,
        'm' => false,
        'M' => false,
    );

    /**
     * The main entry point for the task
     */
    public function main()
    {
        if (null === $this->getRepository()) {
            throw new BuildException('"repository" is required parameter');
        }
        if (null === $this->getBranchname()) {
            throw new BuildException('"branchname" is required parameter');
        }

        // if we are moving branch, we need to know new name
        if ($this->isMove() || $this->isForceMove()) {
            if (null === $this->getNewbranch()) {
                throw new BuildException('"newbranch" is required parameter');
            }
        }

        $client = $this->getGitClient(false, $this->getRepository());
        $command = $client->getCommand('branch');
        $command
            ->setOption('set-upstream', $this->isSetUpstream())
            ->setOption('no-track', $this->isNoTrack())
            ->setOption('force', $this->isForce());
        if ($this->isNoTrack() == false) {
            $command->setOption('track', $this->getTrack());
        }

        // check extra options (delete, move)
        foreach ($this->extraOptions as $option => $flag) {
            if ($flag) {
                $command->setOption($option, true);
            }
        }

        $command->addArgument($this->getBranchname());

        if (null !== $this->getStartPoint()) {
            $command->addArgument($this->getStartPoint());
        }

        if (null !== $this->getNewbranch()) {
            $command->addArgument($this->getNewbranch());
        }

        $this->log('git-branch command: ' . $command->createCommandString(), Project::MSG_INFO);

        try {
            $output = $command->execute();
        } catch (Exception $e) {
            throw new BuildException('Task execution failed.', $e);
        }

        $this->log(
            sprintf('git-branch: branch "%s" repository', $this->getRepository()),
            Project::MSG_INFO
        );
        $this->log('git-branch output: ' . trim($output), Project::MSG_INFO);
    }

    /**
     * @param $flag
     */
    public function setSetUpstream($flag)
    {
        $this->setUpstream = $flag;
    }

    /**
     * @return bool
     */
    public function getSetUpstream()
    {
        return $this->setUpstream;
    }

    /**
     * @return bool
     */
    public function isSetUpstream()
    {
        return $this->getSetUpstream();
    }

    /**
     * @param $flag
     */
    public function setTrack($flag)
    {
        $this->track = $flag;
    }

    /**
     * @return bool
     */
    public function getTrack()
    {
        return $this->track;
    }

    /**
     * @return bool
     */
    public function isTrack()
    {
        return $this->getTrack();
    }

    /**
     * @param $flag
     */
    public function setNoTrack($flag)
    {
        $this->noTrack = $flag;
    }

    /**
     * @return bool
     */
    public function getNoTrack()
    {
        return $this->noTrack;
    }

    /**
     * @return bool
     */
    public function isNoTrack()
    {
        return $this->getNoTrack();
    }

    /**
     * @param $flag
     */
    public function setForce($flag)
    {
        $this->force = $flag;
    }

    /**
     * @return bool
     */
    public function getForce()
    {
        return $this->force;
    }

    /**
     * @return bool
     */
    public function isForce()
    {
        return $this->getForce();
    }

    /**
     * @param $branchname
     */
    public function setBranchname($branchname)
    {
        $this->branchname = $branchname;
    }

    /**
     * @return string
     */
    public function getBranchname()
    {
        return $this->branchname;
    }

    /**
     * @param $startPoint
     */
    public function setStartPoint($startPoint)
    {
        $this->startPoint = $startPoint;
    }

    /**
     * @return string
     */
    public function getStartPoint()
    {
        return $this->startPoint;
    }

    /**
     * @param $flag
     */
    public function setDelete($flag)
    {
        $this->extraOptions['d'] = $flag;
    }

    public function getDelete()
    {
        return $this->extraOptions['d'];
    }

    public function isDelete()
    {
        return $this->getDelete();
    }

    /**
     * @param $flag
     */
    public function setForceDelete($flag)
    {
        $this->extraOptions['D'] = $flag;
    }

    public function getForceDelete()
    {
        return $this->extraOptions['D'];
    }

    /**
     * @param $flag
     */
    public function setMove($flag)
    {
        $this->extraOptions['m'] = $flag;
    }

    public function getMove()
    {
        return $this->extraOptions['m'];
    }

    public function isMove()
    {
        return $this->getMove();
    }

    /**
     * @param $flag
     */
    public function setForceMove($flag)
    {
        $this->extraOptions['M'] = $flag;
    }

    public function getForceMove()
    {
        return $this->extraOptions['M'];
    }

    public function isForceMove()
    {
        return $this->getForceMove();
    }

    /**
     * @param $name
     */
    public function setNewBranch($name)
    {
        $this->newbranch = $name;
    }

    /**
     * @return string
     */
    public function getNewBranch()
    {
        return $this->newbranch;
    }

}
<?php
/*
 *  $Id: 0eb5ad0732bc3acd74df7f9256b37952ddce3212 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/git/GitBaseTask.php';
/**
 * Wrapper around git-checkout
 *
 * @author Victor Farazdagi <simple.square@gmail.com>
 * @version $Id: 0eb5ad0732bc3acd74df7f9256b37952ddce3212 $
 * @package phing.tasks.ext.git
 * @see VersionControl_Git
 * @since 2.4.3
 */
class GitCheckoutTask extends GitBaseTask
{
    /**
     * Branch name
     * @var string
     */
    private $branchname;

    /**
     * If not HEAD, specify starting point
     * @var string
     */
    private $startPoint;

    /**
     * --force, -f key to git-checkout
     * @var boolean
     */
    private $force = false;

    /**
     * --quiet, -q key to git-checkout
     * @var boolean
     */
    private $quiet = false;

    /**
     * When creating a new branch, set up "upstream" configuration.
     * --track key to git-checkout
     * @var boolean
     */
    private $track = false;

    /**
     * Do not set up "upstream" configuration
     * --no-track key to git-checkout
     * @var boolean
     */
    private $noTrack = false;

    /**
     * -b, -B, -m  options to git-checkout
     * Respective task options:
     * create, forceCreate, merge
     * @var array
     */
    private $extraOptions = array(
        'b' => false,
        'B' => false,
        'm' => false,
    );

    /**
     * The main entry point for the task
     */
    public function main()
    {
        if (null === $this->getRepository()) {
            throw new BuildException('"repository" is required parameter');
        }
        if (null === $this->getBranchname()) {
            throw new BuildException('"branchname" is required parameter');
        }

        $client = $this->getGitClient(false, $this->getRepository());
        $command = $client->getCommand('checkout');
        $command
            ->setOption('no-track', $this->isNoTrack())
            ->setOption('q', $this->isQuiet())
            ->setOption('force', $this->isForce())
            ->setOption('b', $this->isCreate())
            ->setOption('B', $this->isForceCreate())
            ->setOption('m', $this->isMerge());
        if ($this->isNoTrack()) {
            $command->setOption('track', $this->isTrack());
        }

        $command->addArgument($this->getBranchname());

        if (null !== $this->getStartPoint()) {
            $command->addArgument($this->getStartPoint());
        }

        $this->log('git-checkout command: ' . $command->createCommandString(), Project::MSG_INFO);

        try {
            $output = $command->execute();
        } catch (Exception $e) {
            throw new BuildException('Task execution failed.', $e);
        }

        $this->log(
            sprintf('git-checkout: checkout "%s" repository', $this->getRepository()),
            Project::MSG_INFO
        );
        $this->log('git-checkout output: ' . trim($output), Project::MSG_INFO);
    }

    /**
     * @param $branchname
     */
    public function setBranchname($branchname)
    {
        $this->branchname = $branchname;
    }

    /**
     * @return string
     */
    public function getBranchname()
    {
        return $this->branchname;
    }

    /**
     * @param $startPoint
     */
    public function setStartPoint($startPoint)
    {
        $this->startPoint = $startPoint;
    }

    /**
     * @return string
     */
    public function getStartPoint()
    {
        return $this->startPoint;
    }

    /**
     * @param $flag
     */
    public function setForce($flag)
    {
        $this->force = $flag;
    }

    /**
     * @return bool
     */
    public function getForce()
    {
        return $this->force;
    }

    /**
     * @return bool
     */
    public function isForce()
    {
        return $this->getForce();
    }

    /**
     * @param $flag
     */
    public function setQuiet($flag)
    {
        $this->quiet = $flag;
    }

    /**
     * @return bool
     */
    public function getQuiet()
    {
        return $this->quiet;
    }

    /**
     * @return bool
     */
    public function isQuiet()
    {
        return $this->getQuiet();
    }

    /**
     * @param $flag
     */
    public function setTrack($flag)
    {
        $this->track = $flag;
    }

    /**
     * @return bool
     */
    public function getTrack()
    {
        return $this->track;
    }

    /**
     * @return bool
     */
    public function isTrack()
    {
        return $this->getTrack();
    }

    /**
     * @param $flag
     */
    public function setNoTrack($flag)
    {
        $this->noTrack = $flag;
    }

    /**
     * @return bool
     */
    public function getNoTrack()
    {
        return $this->noTrack;
    }

    /**
     * @return bool
     */
    public function isNoTrack()
    {
        return $this->getNoTrack();
    }

    /**
     * @param $flag
     */
    public function setCreate($flag)
    {
        $this->extraOptions['b'] = $flag;
    }

    public function getCreate()
    {
        return $this->extraOptions['b'];
    }

    public function isCreate()
    {
        return $this->getCreate();
    }

    // -B flag is not found in all versions of git
    // --force is present everywhere
    /**
     * @param $flag
     */
    public function setForceCreate($flag)
    {
        $this->setForce($flag);
    }

    public function getForceCreate()
    {
        return $this->extraOptions['B'];
    }

    public function isForceCreate()
    {
        return $this->getForceCreate();
    }

    /**
     * @param $flag
     */
    public function setMerge($flag)
    {
        $this->extraOptions['m'] = $flag;
    }

    public function getMerge()
    {
        return $this->extraOptions['m'];
    }

    public function isMerge()
    {
        return $this->getMerge();
    }
}
<?php
/*
 *  $Id: 1ca04138e62374984bd4e04a46fccec605dff5a7 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/git/GitBaseTask.php';
/**
 * Wrapper around git-clone
 *
 * @author Victor Farazdagi <simple.square@gmail.com>
 * @version $Id: 1ca04138e62374984bd4e04a46fccec605dff5a7 $
 * @package phing.tasks.ext.git
 * @see VersionControl_Git
 * @since 2.4.3
 */
class GitCloneTask extends GitBaseTask
{
    /**
     * Whether --depth key should be set for git-clone
     * @var int
     */
    private $depth = 0;

    /**
     * Whether --bare key should be set for git-clone
     * @var bool
     */
    private $isBare = false;

    /**
     * Whether --single-branch key should be set for git-clone
     * @var bool
     */
    private $singleBranch = false;
    
    /**
     * Branch to check out
     * 
     * @var string
     */
    private $branch = "";

    /**
     * Path to target directory
     * @var string
     */
    private $targetPath;

    /**
     * The main entry point for the task
     */
    public function main()
    {
        if (null === $this->getRepository()) {
            throw new BuildException('"repository" is required parameter');
        }

        if (null === $this->getTargetPath()) {
            throw new BuildException('"targetPath" is required parameter');
        }

        $files = @scandir($this->getTargetPath());
        if (isset($files) && is_array($files) && (count($files) > 2)) {
            throw new BuildException(
                sprintf(
                    '"%s" target directory is not empty',
                    $this->getTargetPath()
                )
            );
        }

        try {
            $this->doClone($this->getGitClient(false, getcwd()));
        } catch (Exception $e) {
            throw new BuildException('The remote end hung up unexpectedly', $e);
        }

        $msg = 'git-clone: cloning '
            . ($this->isBare() ? '(bare) ' : '')
            . ($this->hasDepth() ? ' (depth="' . $this->getDepth() . '") ' : '')
            . '"' . $this->getRepository() . '" repository'
            . ' to "' . $this->getTargetPath() . '" directory';
        $this->log($msg, Project::MSG_INFO);
    }

    /**
     * Create a new clone
     *
     * @param VersionControl_Git $client
     *
     * @throws VersionControl_Git_Exception
     */
    protected function doClone(VersionControl_Git $client)
    {
        $command = $client->getCommand('clone')
            ->setOption('q')
            ->setOption('bare', $this->isBare())
            ->setOption('single-branch', $this->isSingleBranch())
            ->setOption('depth', $this->hasDepth() ? $this->getDepth() : false)
            ->setOption('branch', $this->hasBranch() ? $this->getBranch() : false)
            ->addArgument($this->getRepository())
            ->addArgument($this->getTargetPath());

        if (is_dir($this->getTargetPath()) && version_compare('1.6.1.4', $client->getGitVersion(), '>=')) {
            $isEmptyDir = true;
            $entries = scandir($this->getTargetPath());
            foreach ($entries as $entry) {
                if ('.' !== $entry && '..' !== $entry) {
                    $isEmptyDir = false;

                    break;
                }
            }

            if ($isEmptyDir) {
                @rmdir($this->getTargetPath());
            }
        }

        $command->execute();
    }

    /**
     * @return int
     */
    public function getDepth()
    {
        return $this->depth;
    }

    /**
     * @param int $depth
     */
    public function setDepth($depth)
    {
        $this->depth = $depth;
    }

    /**
     * @return bool
     */
    public function hasDepth()
    {
        return (bool) $this->depth;
    }

    /**
     * Get path to target direcotry repo
     *
     * @return string
     */
    public function getTargetPath()
    {
        return $this->targetPath;
    }

    /**
     * Set path to source repo
     *
     * @param  string $targetPath Path to repository used as source
     * @return void
     */
    public function setTargetPath($targetPath)
    {
        $this->targetPath = $targetPath;
    }

    /**
     * Alias @see getBare()
     *
     * @return bool
     */
    public function isBare()
    {
        return $this->getBare();
    }

    /**
     * @return bool
     */
    public function getBare()
    {
        return $this->isBare;
    }

    /**
     * @param $flag
     */
    public function setBare($flag)
    {
        $this->isBare = (bool) $flag;
    }

    /**
     * @return boolean
     */
    public function isSingleBranch()
    {
        return $this->singleBranch;
    }

    /**
     * @param boolean $singleBranch
     */
    public function setSingleBranch($singleBranch)
    {
        $this->singleBranch = $singleBranch;
    }

    /**
     * @return string
     */
    public function getBranch()
    {
        return $this->branch;
    }

    /**
     * @return bool
     */
    public function hasBranch()
    {
        return !empty($this->branch);
    }
    
    /**
     * @param string $branch
     */
    public function setBranch($branch)
    {
        $this->branch = $branch;
    }
}
<?php
/*
 *  $Id: 4160270de534a38cfabe28205a6d9446acb46f04 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/git/GitBaseTask.php';
/**
 * Wrapper around git-commit
 *
 * @package phing.tasks.ext.git
 * @author Jonathan Creasy <jonathan.creasy@gmail.com>
 * @see VersionControl_Git
 * @since 2.4.3
 */
class GitCommitTask extends GitBaseTask
{
    /**
     * @var boolean
     */
    private $allFiles = false;

    /**
     * @var string
     */
    private $message;

    /**
     * @var FileSet[]
     */
    private $filesets = array();

    /**
     * The main entry point for the task
     */
    public function main()
    {
        if (null === $this->getRepository()) {
            throw new BuildException('"repository" is required parameter');
        }

        if ($this->allFiles !== true && empty($this->filesets)) {
            throw new BuildException('"allFiles" cannot be false if no filesets are specified.');
        }

        $options = array();
        if ($this->allFiles === true) {
            $options['all'] = true;
        }

        $arguments = array();
        if ($this->allFiles !== true) {
            foreach ($this->filesets as $fs) {
                $ds = $fs->getDirectoryScanner($this->project);
                $srcFiles = $ds->getIncludedFiles();

                foreach ($srcFiles as $file) {
                    $arguments[] = $file;
                }
            }
        }

        if (!empty($this->message)) {
            $options['message'] = $this->message;
        } else {
            $options['allow-empty-message'] = true;
        }

        try {
            $client = $this->getGitClient(false, $this->getRepository());

            $command = $client->getCommand('commit');
            $command->setArguments($arguments);
            $command->setOptions($options);
            $command->execute();
        } catch (Exception $e) {
            throw new BuildException('The remote end hung up unexpectedly', $e);
        }

        $this->logCommand($options, $arguments);
    }

    /**
     * @param array $options
     * @param array $arguments
     */
    protected function logCommand(array $options, array $arguments)
    {
        $msg = 'git-commit: Executed git commit ';
        foreach ($options as $option => $value) {
            $msg .= ' --' . $option . '=' . $value;
        }

        foreach ($arguments as $argument) {
            $msg .= ' ' . $argument;
        }

        $this->log($msg, Project::MSG_INFO);
    }

    /**
     * @return bool
     */
    public function getAllFiles()
    {
        return $this->allFiles;
    }

    /**
     * @param $flag
     */
    public function setAllFiles($flag)
    {
        $this->allFiles = (bool) $flag;
    }

    /**
     * @return string
     */
    public function getMessage()
    {
        return $this->message;
    }

    /**
     * @param $message
     */
    public function setMessage($message)
    {
        $this->message = $message;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/git/GitBaseTask.php';

/**
 * Wrapper around git-describe
 *
 * @package phing.tasks.ext.git
 * @author  Siad Ardroumli <siad.ardroumli@gmail.com>
 * @see     VersionControl_Git
 */
class GitDescribeTask extends GitBaseTask
{
    /**
     * Use any ref found in .git/refs/. See --all of git-describe
     * @var boolean
     */
    private $all = false;

    /**
     * Use any tag found in .git/refs/tags. See --tags of git-describe
     * @var boolean
     */
    private $tags = false;

    /**
     * Find tag that contains the commit. See --contains of git-describe
     * @var boolean
     */
    private $contains = false;

    /**
     * Use <n> digit object name. See --abbrev of git-describe
     * @var integer
     */
    private $abbrev;

    /**
     * Consider up to <n> most recent tags. See --candidates of git-describe
     * @var integer
     */
    private $candidates;

    /**
     * Always output the long format. See --long of git-describe
     * @var boolean
     */
    private $long = false;

    /**
     * Only consider tags matching the given pattern. See --match of git-describe
     * @var string
     */
    private $match;

    /**
     * Show uniquely abbreviated commit object as fallback. See --always of git-describe
     * @var boolean
     */
    private $always = false;

    /**
     * <committish> argument to git-describe
     * @var string
     */
    private $committish;

    /**
     * Property name to set with output value from git-describe
     * @var string
     */
    private $outputProperty;

    /**
     * The main entry point for the task
     */
    public function main()
    {
        if (null === $this->getRepository()) {
            throw new BuildException('"repository" is required parameter');
        }

        $client = $this->getGitClient(false, $this->getRepository());
        $command = $client->getCommand('describe');
        $command
            ->setOption('all', $this->isAll())
            ->setOption('tags', $this->isTags())
            ->setOption('contains', $this->isContains())
            ->setOption('long', $this->isLong())
            ->setOption('always', $this->isAlways());

        if (null !== $this->getAbbrev()) {
            $command->setOption('abbrev', $this->getAbbrev());
        }
        if (null !== $this->getCandidates()) {
            $command->setOption('candidates', $this->getCandidates());
        }
        if (null !== $this->getMatch()) {
            $command->setOption('match', $this->getMatch());
        }
        if (null !== $this->getCommittish()) {
            $command->addArgument($this->getCommittish());
        }

        try {
            $output = $command->execute();
        } catch (Exception $e) {
            throw new BuildException('Task execution failed');
        }

        if (null !== $this->outputProperty) {
            $this->project->setProperty($this->outputProperty, $output);
        }

        $this->log(
            sprintf('git-describe: recent tags for "%s" repository', $this->getRepository()),
            Project::MSG_INFO
        );
        $this->log('git-describe output: ' . trim($output), Project::MSG_INFO);
    }

    /**
     * @param $flag
     */
    public function setAll($flag)
    {
        $this->all = (bool) $flag;
    }

    /**
     * @return bool
     */
    public function getAll()
    {
        return $this->all;
    }

    /**
     * @return bool
     */
    public function isAll()
    {
        return $this->getAll();
    }

    /**
     * @param $flag
     */
    public function setTags($flag)
    {
        $this->tags = (bool) $flag;
    }

    /**
     * @return bool
     */
    public function getTags()
    {
        return $this->tags;
    }

    /**
     * @return bool
     */
    public function isTags()
    {
        return $this->getTags();
    }

    /**
     * @param $flag
     */
    public function setContains($flag)
    {
        $this->contains = (bool) $flag;
    }

    /**
     * @return bool
     */
    public function getContains()
    {
        return $this->contains;
    }

    /**
     * @return bool
     */
    public function isContains()
    {
        return $this->getContains();
    }

    /**
     * @param $length
     */
    public function setAbbrev($length)
    {
        $this->abbrev = (int) $length;
    }

    /**
     * @return int
     */
    public function getAbbrev()
    {
        return $this->abbrev;
    }

    /**
     * @param $count
     */
    public function setCandidates($count)
    {
        $this->candidates = (int) $count;
    }

    /**
     * @return int
     */
    public function getCandidates()
    {
        return $this->candidates;
    }

    /**
     * @param $flag
     */
    public function setLong($flag)
    {
        $this->long = (bool) $flag;
    }

    /**
     * @return bool
     */
    public function getLong()
    {
        return $this->long;
    }

    /**
     * @return bool
     */
    public function isLong()
    {
        return $this->getLong();
    }

    /**
     * @param $pattern
     */
    public function setMatch($pattern)
    {
        $this->match = $pattern;
    }

    /**
     * @return string
     */
    public function getMatch()
    {
        return $this->match;
    }

    /**
     * @param $flag
     */
    public function setAlways($flag)
    {
        $this->always = (bool) $flag;
    }

    /**
     * @return bool
     */
    public function getAlways()
    {
        return $this->always;
    }

    /**
     * @return bool
     */
    public function isAlways()
    {
        return $this->getAlways();
    }

    /**
     * @param $object
     */
    public function setCommittish($object)
    {
        $this->committish = $object;
    }

    /**
     * @return string
     */
    public function getCommittish()
    {
        return $this->committish;
    }

    /**
     * @param string $prop
     */
    public function setOutputProperty($prop)
    {
        $this->outputProperty = $prop;
    }
}
<?php
/*
 *  $Id: 0de7b4504804e074f617010ef52604d1da1e76b7 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/git/GitBaseTask.php';

/**
 * Wrapper aroung git-fetch
 *
 * @author Victor Farazdagi <simple.square@gmail.com>
 * @version $Id: 0de7b4504804e074f617010ef52604d1da1e76b7 $
 * @package phing.tasks.ext.git
 * @see VersionControl_Git
 * @since 2.4.3
 */
class GitFetchTask extends GitBaseTask
{
    /**
     * --force, -f key to git-fetch
     * @var boolean
     */
    private $force = false;

    /**
     * --quiet, -q key to git-fetch
     * @var boolean
     */
    private $quiet = false;

    /**
     * Fetch all remotes
     * --all key to git-fetch
     * @var boolean
     */
    private $allRemotes = false;

    /**
     * Keep downloaded pack
     * --keep key to git-fetch
     * @var boolean
     */
    private $keepFiles = false;

    /**
     * After fetching, remove any remote tracking branches which no longer
     * exist on the remote.
     * --prune key to git fetch
     * @var boolean
     */
    private $prune = false;

    /**
     * Disable/enable automatic tag following
     * --no-tags key to git-fetch
     * @var boolean
     */
    private $noTags = false;

    /**
     * Fetch all tags (even not reachable from branch heads)
     * --tags key to git-fetch
     * @var boolean
     */
    private $tags = false;

    /**
     * <group> argument to git-fetch
     * @var string
     */
    private $group;

    /**
     * <repository> argument to git-fetch
     * @var string
     */
    private $source = 'origin';

    /**
     * <refspec> argument to git-fetch
     * @var string
     */
    private $refspec;

    /**
     * The main entry point for the task
     */
    public function main()
    {
        if (null === $this->getRepository()) {
            throw new BuildException('"repository" is required parameter');
        }

        $client = $this->getGitClient(false, $this->getRepository());
        $command = $client->getCommand('fetch');
        $command
            ->setOption('tags', $this->isTags())
            ->setOption('no-tags', $this->isNoTags())
            ->setOption('prune', $this->isPrune())
            ->setOption('keep', $this->isKeepFiles())
            ->setOption('q', $this->isQuiet())
            ->setOption('force', $this->isForce());

        // set operation target
        if ($this->isAllRemotes()) { // --all
            $command->setOption('all', true);
        } elseif ($this->getGroup()) { // <group>
            $command->addArgument($this->getGroup());
        } elseif ($this->getSource()) { // <repository> [<refspec>]
            $command->addArgument($this->getSource());
            if ($this->getRefspec()) {
                $command->addArgument($this->getRefspec());
            }
        } else {
            throw new BuildException('No remote repository specified');
        }

        $this->log('git-fetch command: ' . $command->createCommandString(), Project::MSG_INFO);

        try {
            $output = $command->execute();
        } catch (Exception $e) {
            throw new BuildException('Task execution failed.', $e);
        }

        $this->log(
            sprintf('git-fetch: branch "%s" repository', $this->getRepository()),
            Project::MSG_INFO
        );
        $this->log('git-fetch output: ' . trim($output), Project::MSG_INFO);
    }

    /**
     * @param $flag
     */
    public function setForce($flag)
    {
        $this->force = $flag;
    }

    /**
     * @return bool
     */
    public function getForce()
    {
        return $this->force;
    }

    /**
     * @return bool
     */
    public function isForce()
    {
        return $this->getForce();
    }

    /**
     * @param $flag
     */
    public function setQuiet($flag)
    {
        $this->quiet = $flag;
    }

    /**
     * @return bool
     */
    public function getQuiet()
    {
        return $this->quiet;
    }

    /**
     * @return bool
     */
    public function isQuiet()
    {
        return $this->getQuiet();
    }

    /**
     * @param $flag
     */
    public function setAll($flag)
    {
        $this->allRemotes = $flag;
    }

    /**
     * @return bool
     */
    public function getAll()
    {
        return $this->allRemotes;
    }

    /**
     * @return bool
     */
    public function isAllRemotes()
    {
        return $this->getAll();
    }

    /**
     * @param $flag
     */
    public function setKeep($flag)
    {
        $this->keepFiles = $flag;
    }

    /**
     * @return bool
     */
    public function getKeep()
    {
        return $this->keepFiles;
    }

    /**
     * @return bool
     */
    public function isKeepFiles()
    {
        return $this->getKeep();
    }

    /**
     * @param $flag
     */
    public function setPrune($flag)
    {
        $this->prune = $flag;
    }

    /**
     * @return bool
     */
    public function getPrune()
    {
        return $this->prune;
    }

    /**
     * @return bool
     */
    public function isPrune()
    {
        return $this->getPrune();
    }

    /**
     * @param $flag
     */
    public function setNoTags($flag)
    {
        $this->noTags = $flag;
    }

    /**
     * @return bool
     */
    public function getNoTags()
    {
        return $this->noTags;
    }

    /**
     * @return bool
     */
    public function isNoTags()
    {
        return $this->getNoTags();
    }

    /**
     * @param $flag
     */
    public function setTags($flag)
    {
        $this->tags = $flag;
    }

    /**
     * @return bool
     */
    public function getTags()
    {
        return $this->tags;
    }

    /**
     * @return bool
     */
    public function isTags()
    {
        return $this->getTags();
    }

    /**
     * @param $source
     */
    public function setSource($source)
    {
        $this->source = $source;
    }

    /**
     * @return string
     */
    public function getSource()
    {
        return $this->source;
    }

    /**
     * @param $spec
     */
    public function setRefspec($spec)
    {
        $this->refspec = $spec;
    }

    /**
     * @return string
     */
    public function getRefspec()
    {
        return $this->refspec;
    }

    /**
     * @param $group
     */
    public function setGroup($group)
    {
        $this->group = $group;
    }

    /**
     * @return string
     */
    public function getGroup()
    {
        return $this->group;
    }

}
<?php
/*
 *  $Id: 5201d8d2dfb0c6ffb371ee85b8aa4bfc731b00c4 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/git/GitBaseTask.php';
/**
 * Wrapper around git-gc
 *
 * @author Victor Farazdagi <simple.square@gmail.com>
 * @version $Id: 5201d8d2dfb0c6ffb371ee85b8aa4bfc731b00c4 $
 * @package phing.tasks.ext.git
 * @see VersionControl_Git
 * @since 2.4.3
 */
class GitGcTask extends GitBaseTask
{
    /**
     * --aggressive key to git-gc
     * @var boolean
     */
    private $isAggressive = false;

    /**
     * --auto key to git-gc
     * @var boolean
     */
    private $isAuto = false;

    /**
     * --no-prune key to git-gc
     * @var boolean
     */
    private $noPrune = false;

    /**
     * --prune=<date>option of git-gc
     * @var string
     */
    private $prune = '2.weeks.ago';

    /**
     * The main entry point for the task
     */
    public function main()
    {
        if (null === $this->getRepository()) {
            throw new BuildException('"repository" is required parameter');
        }

        $client = $this->getGitClient(false, $this->getRepository());
        $command = $client->getCommand('gc');
        $command
            ->setOption('aggressive', $this->isAggressive())
            ->setOption('auto', $this->isAuto())
            ->setOption('no-prune', $this->isNoPrune());
        if ($this->isNoPrune() == false) {
            $command->setOption('prune', $this->getPrune());
        }

        // suppress output
        $command->setOption('q');

        $this->log('git-gc command: ' . $command->createCommandString(), Project::MSG_INFO);

        try {
            $command->execute();
        } catch (Exception $e) {
            throw new BuildException('Task execution failed', $e);
        }

        $this->log(
            sprintf('git-gc: cleaning up "%s" repository', $this->getRepository()),
            Project::MSG_INFO
        );
    }

    /**
     * @see getAggressive()
     */
    public function isAggressive()
    {
        return $this->getAggressive();
    }

    /**
     * @return bool
     */
    public function getAggressive()
    {
        return $this->isAggressive;
    }

    /**
     * @param $flag
     */
    public function setAggressive($flag)
    {
        $this->isAggressive = (bool) $flag;
    }

    /**
     * @see getAuto()
     */
    public function isAuto()
    {
        return $this->getAuto();
    }

    /**
     * @return bool
     */
    public function getAuto()
    {
        return $this->isAuto;
    }

    /**
     * @param $flag
     */
    public function setAuto($flag)
    {
        $this->isAuto = (bool) $flag;
    }

    /**
     * @see NoPrune()
     */
    public function isNoPrune()
    {
        return $this->getNoPrune();
    }

    /**
     * @return bool
     */
    public function getNoPrune()
    {
        return $this->noPrune;
    }

    /**
     * @param $flag
     */
    public function setNoPrune($flag)
    {
        $this->noPrune = (bool) $flag;
    }

    /**
     * @return string
     */
    public function getPrune()
    {
        return $this->prune;
    }

    /**
     * @param $date
     */
    public function setPrune($date)
    {
        $this->prune = $date;
    }

}
<?php
/*
 *  $Id: 8e8b0c6caa7260a0bb563b71f8af4291067cbef0 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/BuildException.php';
require_once 'phing/tasks/ext/git/GitBaseTask.php';

/**
 * Repository initialization task
 *
 * @author Victor Farazdagi <simple.square@gmail.com>
 * @version $Id: 8e8b0c6caa7260a0bb563b71f8af4291067cbef0 $
 * @package phing.tasks.ext.git
 * @see VersionControl_Git
 * @since 2.4.3
 */
class GitInitTask extends GitBaseTask
{

    /**
     * Whether --bare key should be set for git-init
     * @var string
     */
    private $isBare = false;

    /**
     * The main entry point for the task
     */
    public function main()
    {
        if (null === $this->getRepository()) {
            throw new BuildException('"repository" is required parameter');
        }

        $client = $this->getGitClient();
        $client->initRepository($this->isBare());

        $msg = 'git-init: initializing '
            . ($this->isBare() ? '(bare) ' : '')
            . '"' . $this->getRepository() . '" repository';
        $this->log($msg, Project::MSG_INFO);
    }

    /**
     * Alias @see getBare()
     *
     * @return string
     */
    public function isBare()
    {
        return $this->getBare();
    }

    /**
     * @return string
     */
    public function getBare()
    {
        return $this->isBare;
    }

    /**
     * @param $flag
     */
    public function setBare($flag)
    {
        $this->isBare = (bool) $flag;
    }
}
<?php
/*
 *  $Id: 8b23ec7a8c95625a37c67d7cba50df578b95b2b6 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/git/GitBaseTask.php';

/**
 * Wrapper aroung git-log
 *
 * @author Evan Kaufman <evan@digitalflophouse.com>
 * @author Victor Farazdagi <simple.square@gmail.com>
 * @version $Id: 8b23ec7a8c95625a37c67d7cba50df578b95b2b6 $
 * @package phing.tasks.ext.git
 * @see VersionControl_Git
 * @since 2.4.5
 */
class GitLogTask extends GitBaseTask
{
    /**
     * Generate a diffstat. See --stat of git-log
     * @var string|boolean
     */
    private $stat = false;

    /**
     * Names + status of changed files. See --name-status of git-log
     * @var boolean
     */
    private $nameStatus = false;

    /**
     * Number of commits to show. See -<n>|-n|--max-count of git-log
     * @var integer
     */
    private $maxCount;

    /**
     * Don't show commits with more than one parent. See --no-merges of git-log
     * @var boolean
     */
    private $noMerges = false;

    /**
     * Commit format. See --format of git-log
     * @var string
     */
    private $format = 'medium';

    /**
     * Date format. See --date of git-log
     * @var string
     */
    private $date;

    /**
     * <since> argument to git-log
     * @var string
     */
    private $since;

    /**
     * <until> argument to git-log
     * @var string
     */
    private $until;

    /**
     * <path> arguments to git-log
     * Accepts one or more paths delimited by PATH_SEPARATOR
     * @var string
     */
    private $paths;

    /**
     * Property name to set with output value from git-log
     * @var string
     */
    private $outputProperty;

    /**
     * The main entry point for the task
     */
    public function main()
    {
        if (null === $this->getRepository()) {
            throw new BuildException('"repository" is required parameter');
        }

        $client = $this->getGitClient(false, $this->getRepository());
        $command = $client->getCommand('log');
        $command
            ->setOption('stat', $this->getStat())
            ->setOption('name-status', $this->isNameStatus())
            ->setOption('no-merges', $this->isNoMerges())
            ->setOption('format', $this->getFormat());

        if (null !== $this->getMaxCount()) {
            $command->setOption('max-count', $this->getMaxCount());
        }

        if (null !== $this->getDate()) {
            $command->setOption('date', $this->getDate());
        }

        if (null !== $this->getSince()) {
            $command->setOption('since', $this->getSince());
        }
        
        if (null !== $this->getUntil()) {
            $command->setOption('until', $this->getUntil());
        }

        $command->addDoubleDash(true);
        if (null !== $this->getPaths()) {
            $command->addDoubleDash(false);
            $paths = explode(PATH_SEPARATOR, $this->getPaths());
            foreach ($paths as $path) {
                $command->addArgument($path);
            }
        }

        $this->log('git-log command: ' . $command->createCommandString(), Project::MSG_INFO);

        try {
            $output = $command->execute();
        } catch (Exception $e) {
            throw new BuildException('Task execution failed', $e);
        }

        if (null !== $this->outputProperty) {
            $this->project->setProperty($this->outputProperty, $output);
        }

        $this->log(
            sprintf('git-log: commit log for "%s" repository', $this->getRepository()),
            Project::MSG_INFO
        );
        $this->log('git-log output: ' . trim($output), Project::MSG_INFO);
    }

    /**
     * @param $stat
     */
    public function setStat($stat)
    {
        $this->stat = $stat;
    }

    /**
     * @return bool|string
     */
    public function getStat()
    {
        return $this->stat;
    }

    /**
     * @param $flag
     */
    public function setNameStatus($flag)
    {
        $this->nameStatus = (boolean) $flag;
    }

    /**
     * @return bool
     */
    public function getNameStatus()
    {
        return $this->nameStatus;
    }

    /**
     * @return bool
     */
    public function isNameStatus()
    {
        return $this->getNameStatus();
    }

    /**
     * @param $count
     */
    public function setMaxCount($count)
    {
        $this->maxCount = (int) $count;
    }

    /**
     * @return int
     */
    public function getMaxCount()
    {
        return $this->maxCount;
    }

    /**
     * @param $flag
     */
    public function setNoMerges($flag)
    {
        $this->noMerges = (bool) $flag;
    }

    /**
     * @return bool
     */
    public function getNoMerges()
    {
        return $this->noMerges;
    }

    /**
     * @return bool
     */
    public function isNoMerges()
    {
        return $this->getNoMerges();
    }

    /**
     * @param $format
     */
    public function setFormat($format)
    {
        $this->format = $format;
    }

    /**
     * @return string
     */
    public function getFormat()
    {
        return $this->format;
    }

    /**
     * @param $date
     */
    public function setDate($date)
    {
        $this->date = $date;
    }

    /**
     * @return string
     */
    public function getDate()
    {
        return $this->date;
    }

    /**
     * @param $since
     */
    public function setSince($since)
    {
        $this->since = $since;
    }

    /**
     * @return string
     */
    public function getSince()
    {
        return $this->since;
    }

    /**
     * @param $after
     */
    public function setAfter($after)
    {
        $this->setSince($after);
    }

    /**
     * @param $until
     */
    public function setUntil($until)
    {
        $this->until = $until;
    }

    /**
     * @return string
     */
    public function getUntil()
    {
        return $this->until;
    }

    /**
     * @param $before
     */
    public function setBefore($before)
    {
        $this->setUntil($before);
    }

    /**
     * @param $paths
     */
    public function setPaths($paths)
    {
        $this->paths = $paths;
    }

    /**
     * @return string
     */
    public function getPaths()
    {
        return $this->paths;
    }

    /**
     * @param $prop
     */
    public function setOutputProperty($prop)
    {
        $this->outputProperty = $prop;
    }

}
<?php
/*
 *  $Id: e5d20b926ab512d72bfc19ac2f1ccc80d09f036c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/git/GitBaseTask.php';

/**
 * Wrapper aroung git-merge
 *
 * @author Victor Farazdagi <simple.square@gmail.com>
 * @version $Id: e5d20b926ab512d72bfc19ac2f1ccc80d09f036c $
 * @package phing.tasks.ext.git
 * @see VersionControl_Git
 * @since 2.4.3
 * @link http://www.kernel.org/pub/software/scm/git/docs/git-merge.html
 */
class GitMergeTask extends GitBaseTask
{
    /**
     * <commit> of git-merge
     * @var string
     */
    private $remote;

    /**
     * Commit message
     * @var string
     */
    private $message;

    /**
     * Merge strategy. See -s <strategy> of git-merge
     * Available strategies are: octopus ours recursive resolve subtree
     * @var string
     */
    private $strategy;

    /**
     * -X or --strategy-option of git-merge
     * @var string
     */
    private $strategyOption;

    /**
     * --commit key of git-merge
     * @var boolean
     */
    private $commit = false;

    /**
     * --no-commit key of git-merge
     * @var boolean
     */
    private $noCommit = false;

    /**
     * --ff --no-ff keys to git-merge
     * @var boolean
     */
    private $fastForwardCommit = false;

    /**
     * --quiet, -q key to git-merge
     * @var boolean
     */
    private $quiet = false;

    /**
     * Valid merge strategies
     * @var array
     */
    private $validStrategies = array(
        'octopus',
        'ours',
        'theirs',
        'recursive',
        'resolve',
        'subtree'
    );

    /**
     * The main entry point for the task
     */
    public function main()
    {
        if (null === $this->getRepository()) {
            throw new BuildException('"repository" is required parameter');
        }
        $remotes = trim($this->getRemote());
        if (null === $remotes || '' === $remotes) {
            throw new BuildException('"remote" is required parameter');
        }

        $client = $this->getGitClient(false, $this->getRepository());
        $command = $client->getCommand('merge');
        $command
            ->setOption('commit', $this->isCommit())
            ->setOption('q', $this->isQuiet());

        if ($this->getMessage()) {
            $command->setOption('message', $this->getMessage());
        }

        if (!$this->isCommit()) {
            $command->setOption('no-commit', $this->isNoCommit());
        }

        if ($this->isFastForwardCommit()) {
            $command->setOption('no-ff', true);
        }

        $strategy = $this->getStrategy();
        if ($strategy) {
            // check if strategy is valid
            if (false === in_array($strategy, $this->validStrategies)) {
                throw new BuildException(
                    "Could not find merge strategy '" . $strategy . "'\n" .
                    "Available strategies are: " . implode(', ', $this->validStrategies));
            }
            $command->setOption('strategy', $strategy);
            if ($this->getStrategyOption()) {
                $command->setOption(
                    'strategy-option',
                    $this->getStrategyOption()
                );
            }
        }

        $remotes = explode(' ', $this->getRemote());
        foreach ($remotes as $remote) {
            $command->addArgument($remote);
        }

        $this->log('git-merge command: ' . $command->createCommandString(), Project::MSG_INFO);

        try {
            $output = $command->execute();
        } catch (Exception $e) {
            throw new BuildException('Task execution failed.', $e);
        }

        $this->log(
            sprintf('git-merge: replaying "%s" commits', $this->getRemote()),
            Project::MSG_INFO
        );
        $this->log('git-merge output: ' . trim($output), Project::MSG_INFO);

    }

    /**
     * @param $remote
     */
    public function setRemote($remote)
    {
        $this->remote = $remote;
    }

    /**
     * @return string
     */
    public function getRemote()
    {
        return $this->remote;
    }

    /**
     * @param $message
     */
    public function setMessage($message)
    {
        $this->message = $message;
    }

    /**
     * @return string
     */
    public function getMessage()
    {
        return $this->message;
    }

    /**
     * @param $strategy
     */
    public function setStrategy($strategy)
    {
        $this->strategy = $strategy;
    }

    /**
     * @return string
     */
    public function getStrategy()
    {
        return $this->strategy;
    }

    /**
     * @param $strategyOption
     */
    public function setStrategyOption($strategyOption)
    {
        $this->strategyOption = $strategyOption;
    }

    /**
     * @return string
     */
    public function getStrategyOption()
    {
        return $this->strategyOption;
    }

    /**
     * @param $flag
     */
    public function setQuiet($flag)
    {
        $this->quiet = $flag;
    }

    /**
     * @return bool
     */
    public function getQuiet()
    {
        return $this->quiet;
    }

    /**
     * @return bool
     */
    public function isQuiet()
    {
        return $this->getQuiet();
    }

    /**
     * @param $flag
     */
    public function setCommit($flag)
    {
        $this->commit = (boolean) $flag;
    }

    /**
     * @return bool
     */
    public function getCommit()
    {
        return $this->commit;
    }

    /**
     * @return bool
     */
    public function isCommit()
    {
        return $this->getCommit();
    }

    /**
     * @param $flag
     */
    public function setNoCommit($flag)
    {
        $this->noCommit = (boolean) $flag;
    }

    /**
     * @return bool
     */
    public function getNoCommit()
    {
        return $this->noCommit;
    }

    /**
     * @return bool
     */
    public function isNoCommit()
    {
        return $this->getNoCommit();
    }

    /**
     * @param $flag
     */
    public function setFastForwardCommit($flag)
    {
        $this->fastForwardCommit = $flag;
    }

    /**
     * @return bool
     */
    public function getFastForwardCommit()
    {
        return $this->fastForwardCommit;
    }

    /**
     * @return bool
     */
    public function isFastForwardCommit()
    {
        return $this->getFastForwardCommit();
    }
}
<?php
/*
 *  $Id: 3ca88dd906a2b8b95edd6c11265984a9d57dbde5 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/git/GitBaseTask.php';

/**
 * Wrapper aroung git-pull
 *
 * @author Victor Farazdagi <simple.square@gmail.com>
 * @version $Id: 3ca88dd906a2b8b95edd6c11265984a9d57dbde5 $
 * @package phing.tasks.ext.git
 * @see VersionControl_Git
 * @since 2.4.3
 */
class GitPullTask extends GitBaseTask
{
    /**
     * <repository> argument to git-pull
     * @var string
     */
    private $source = 'origin';

    /**
     * <refspec> argument to git-pull
     * @var string
     */
    private $refspec;

    /**
     * --rebase key to git-pull
     * @var boolean
     */
    private $rebase = false;

    /**
     * --no-rebase key to git-pull
     * Allow to override --rebase (if set to default true in configuration)
     * @var boolean
     */
    private $noRebase = false;

    /**
     * Merge strategy. See -s <strategy> of git-pull
     * @var string
     */
    private $strategy;

    /**
     * -X or --strategy-option of git-pull
     * @var string
     */
    private $strategyOption;

    /**
     * Fetch all remotes
     * --all key to git-pull
     * @var boolean
     */
    private $allRemotes = false;

    /**
     * --append key to git-pull
     * @var boolean
     */
    private $append = false;

    /**
     * Keep downloaded pack
     * --keep key to git-pull
     * @var boolean
     */
    private $keepFiles = false;

    /**
     * Disable/enable automatic tag following
     * --no-tags key to git-pull
     * @var boolean
     */
    private $noTags = false;

    /**
     * Fetch all tags (even not reachable from branch heads)
     * --tags key to git-pull
     * @var boolean
     */
    private $tags = false;

    /**
     * --quiet, -q key to git-pull
     * @var boolean
     */
    private $quiet = true;

    /**
     * --force, -f key to git-pull
     * @var boolean
     */
    private $force = false;

    /**
     * Valid merge strategies
     * @var array
     */
    private $validStrategies = array(
        'octopus',
        'ours',
        'recursive',
        'resolve',
        'subtree'
    );

    /**
     * The main entry point for the task
     */
    public function main()
    {
        if (null === $this->getRepository()) {
            throw new BuildException('"repository" is required parameter');
        }

        $client = $this->getGitClient(false, $this->getRepository());
        $command = $client->getCommand('pull');
        $command
            ->setOption('rebase', $this->isRebase());

        if (!$this->isRebase()) {
            $command->setOption('no-rebase', $this->isNoRebase());
        }

        $strategy = $this->getStrategy();
        if ($strategy) {
            // check if strategy is valid
            if (false === in_array($strategy, $this->validStrategies)) {
                throw new BuildException(
                    "Could not find merge strategy '" . $strategy . "'\n" .
                    "Available strategies are: " . implode(', ', $this->validStrategies));
            }
            $command->setOption('strategy', $strategy);
            if ($this->getStrategyOption()) {
                $command->setOption(
                    'strategy-option',
                    $this->getStrategyOption()
                );
            }
        }

        // order of arguments is important
        $command
            ->setOption('tags', $this->isTags())
            ->setOption('no-tags', $this->isNoTags())
            ->setOption('keep', $this->isKeepFiles())
            ->setOption('append', $this->isAppend())
            ->setOption('q', $this->isQuiet())
            ->setOption('force', $this->isForce());

        // set operation target
        if ($this->isAllRemotes()) { // --all
            $command->setOption('all', true);
            $this->log('git-pull: fetching from all remotes', Project::MSG_INFO);
        } elseif ($this->getSource()) { // <repository> [<refspec>]
            $command->addArgument($this->getSource());
            if ($this->getRefspec()) {
                $command->addArgument($this->getRefspec());
            }
            $this->log(
                sprintf(
                    'git-pull: pulling from %s %s',
                    $this->getSource(),
                    $this->getRefspec()
                ),
                Project::MSG_INFO
            );
        } else {
            throw new BuildException('No source repository specified');
        }

        $this->log('git-pull command: ' . $command->createCommandString(), Project::MSG_INFO);

        try {
            $output = $command->execute();
        } catch (Exception $e) {
            throw new BuildException('Task execution failed.', $e);
        }

        $this->log('git-pull: complete', Project::MSG_INFO);
        $this->log('git-pull output: ' . trim($output), Project::MSG_INFO);

    }

    /**
     * @param $strategy
     */
    public function setStrategy($strategy)
    {
        $this->strategy = $strategy;
    }

    /**
     * @return string
     */
    public function getStrategy()
    {
        return $this->strategy;
    }

    /**
     * @param $strategyOption
     */
    public function setStrategyOption($strategyOption)
    {
        $this->strategyOption = $strategyOption;
    }

    /**
     * @return string
     */
    public function getStrategyOption()
    {
        return $this->strategyOption;
    }

    /**
     * @param $source
     */
    public function setSource($source)
    {
        $this->source = $source;
    }

    /**
     * @return string
     */
    public function getSource()
    {
        return $this->source;
    }

    /**
     * @param $spec
     */
    public function setRefspec($spec)
    {
        $this->refspec = $spec;
    }

    /**
     * @return string
     */
    public function getRefspec()
    {
        return $this->refspec;
    }

    /**
     * @param $flag
     */
    public function setAll($flag)
    {
        $this->allRemotes = $flag;
    }

    /**
     * @return bool
     */
    public function getAll()
    {
        return $this->allRemotes;
    }

    /**
     * @return bool
     */
    public function isAllRemotes()
    {
        return $this->getAll();
    }

    /**
     * @param $flag
     */
    public function setAppend($flag)
    {
        $this->append = (boolean) $flag;
    }

    /**
     * @return bool
     */
    public function getAppend()
    {
        return $this->append;
    }

    /**
     * @return bool
     */
    public function isAppend()
    {
        return $this->getAppend();
    }

    /**
     * @param $flag
     */
    public function setKeep($flag)
    {
        $this->keepFiles = $flag;
    }

    /**
     * @return bool
     */
    public function getKeep()
    {
        return $this->keepFiles;
    }

    /**
     * @return bool
     */
    public function isKeepFiles()
    {
        return $this->getKeep();
    }

    /**
     * @param $flag
     */
    public function setNoTags($flag)
    {
        $this->noTags = $flag;
    }

    /**
     * @return bool
     */
    public function getNoTags()
    {
        return $this->noTags;
    }

    /**
     * @return bool
     */
    public function isNoTags()
    {
        return $this->getNoTags();
    }

    /**
     * @param $flag
     */
    public function setTags($flag)
    {
        $this->tags = $flag;
    }

    /**
     * @return bool
     */
    public function getTags()
    {
        return $this->tags;
    }

    /**
     * @return bool
     */
    public function isTags()
    {
        return $this->getTags();
    }

    /**
     * @param $flag
     */
    public function setQuiet($flag)
    {
        $this->quiet = $flag;
    }

    /**
     * @return bool
     */
    public function getQuiet()
    {
        return $this->quiet;
    }

    /**
     * @return bool
     */
    public function isQuiet()
    {
        return $this->getQuiet();
    }

    /**
     * @param $flag
     */
    public function setRebase($flag)
    {
        $this->rebase = (boolean) $flag;
    }

    /**
     * @return bool
     */
    public function getRebase()
    {
        return $this->rebase;
    }

    /**
     * @return bool
     */
    public function isRebase()
    {
        return $this->getRebase();
    }

    /**
     * @param $flag
     */
    public function setNoRebase($flag)
    {
        $this->noRebase = (boolean) $flag;
    }

    /**
     * @return bool
     */
    public function getNoRebase()
    {
        return $this->noRebase;
    }

    /**
     * @return bool
     */
    public function isNoRebase()
    {
        return $this->getNoRebase();
    }

    /**
     * @param $flag
     */
    public function setForce($flag)
    {
        $this->force = $flag;
    }

    /**
     * @return bool
     */
    public function getForce()
    {
        return $this->force;
    }

    /**
     * @return bool
     */
    public function isForce()
    {
        return $this->getForce();
    }

}
<?php
/*
 *  $Id: 8406c9cfd560b995db09e9888334355641cd3975 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/git/GitBaseTask.php';

/**
 * Wrapper aroung git-push
 *
 * @author Victor Farazdagi <simple.square@gmail.com>
 * @version $Id: 8406c9cfd560b995db09e9888334355641cd3975 $
 * @package phing.tasks.ext.git
 * @see VersionControl_Git
 * @since 2.4.3
 * @link http://www.kernel.org/pub/software/scm/git/docs/git-push.html
 */
class GitPushTask extends GitBaseTask
{
    /**
     * Instead of naming each ref to push, specifies that all refs
     * --all key to git-push
     * @var boolean
     */
    private $allRemotes = false;

    /**
     * Mirror to remote repository
     * --mirror key to git-push
     * @var boolean
     */
    private $mirror = false;

    /**
     * Same as prefixing repos with colon
     * --delete argument to git-push
     * @var string
     */
    private $delete = false;

    /**
     * Push all refs under refs/tags
     * --tags key to git-fetch
     * @var boolean
     */
    private $tags = false;

    /**
     * <repository> argument to git-push
     * @var string
     */
    private $destination = 'origin';

    /**
     * <refspec> argument to git-push
     * @var string
     */
    private $refspec;

    /**
     * --force, -f key to git-push
     * @var boolean
     */
    private $force = false;

    /**
     * --quiet, -q key to git-push
     * @var boolean
     */
    private $quiet = true;

    /**
     * The main entry point for the task
     */
    public function main()
    {
        if (null === $this->getRepository()) {
            throw new BuildException('"repository" is required parameter');
        }

        $client = $this->getGitClient(false, $this->getRepository());
        $command = $client->getCommand('push');
        $command
            ->setOption('tags', $this->isTags())
            ->setOption('mirror', $this->isMirror())
            ->setOption('delete', $this->isDelete())
            ->setOption('q', $this->isQuiet())
            ->setOption('force', $this->isForce());

        // set operation target
        if ($this->isAllRemotes()) { // --all
            $command->setOption('all', true);
            $this->log('git-push: push to all refs', Project::MSG_INFO);
        } elseif ($this->isMirror()) { // <repository> [<refspec>]
            $command->setOption('mirror', true);
            $this->log('git-push: mirror all refs', Project::MSG_INFO);
        } elseif ($this->getDestination()) { // <repository> [<refspec>]
            $command->addArgument($this->getDestination());
            if ($this->getRefspec()) {
                $command->addArgument($this->getRefspec());
            }
            $this->log(
                sprintf(
                    'git-push: pushing to %s %s',
                    $this->getDestination(),
                    $this->getRefspec()
                ),
                Project::MSG_INFO
            );
        } else {
            throw new BuildException('At least one destination must be provided');
        }

        $this->log('git-push command: ' . $command->createCommandString(), Project::MSG_INFO);

        try {
            $output = $command->execute();
        } catch (Exception $e) {
            throw new BuildException('Task execution failed.', $e);
        }

        $this->log('git-push: complete', Project::MSG_INFO);
        if ($this->isDelete()) {
            $this->log('git-push: branch delete requested', Project::MSG_INFO);
        }
        $this->log('git-push output: ' . trim($output), Project::MSG_INFO);
    }

    /**
     * @param $flag
     */
    public function setAll($flag)
    {
        $this->allRemotes = $flag;
    }

    /**
     * @return bool
     */
    public function getAll()
    {
        return $this->allRemotes;
    }

    /**
     * @return bool
     */
    public function isAllRemotes()
    {
        return $this->getAll();
    }

    /**
     * @param $flag
     */
    public function setMirror($flag)
    {
        $this->mirror = (boolean) $flag;
    }

    /**
     * @return bool
     */
    public function getMirror()
    {
        return $this->mirror;
    }

    /**
     * @return bool
     */
    public function isMirror()
    {
        return $this->getMirror();
    }

    /**
     * @param $flag
     */
    public function setDelete($flag)
    {
        $this->delete = (boolean) $flag;
    }

    /**
     * @return string
     */
    public function getDelete()
    {
        return $this->delete;
    }

    /**
     * @return string
     */
    public function isDelete()
    {
        return $this->getDelete();
    }

    /**
     * @param $flag
     */
    public function setTags($flag)
    {
        $this->tags = $flag;
    }

    /**
     * @return bool
     */
    public function getTags()
    {
        return $this->tags;
    }

    /**
     * @return bool
     */
    public function isTags()
    {
        return $this->getTags();
    }

    /**
     * @param $destination
     */
    public function setDestination($destination)
    {
        $this->destination = $destination;
    }

    /**
     * @return string
     */
    public function getDestination()
    {
        return $this->destination;
    }

    /**
     * @param $spec
     */
    public function setRefspec($spec)
    {
        $this->refspec = $spec;
    }

    /**
     * @return string
     */
    public function getRefspec()
    {
        return $this->refspec;
    }

    /**
     * @param $flag
     */
    public function setForce($flag)
    {
        $this->force = $flag;
    }

    /**
     * @return bool
     */
    public function getForce()
    {
        return $this->force;
    }

    /**
     * @return bool
     */
    public function isForce()
    {
        return $this->getForce();
    }

    /**
     * @param $flag
     */
    public function setQuiet($flag)
    {
        $this->quiet = $flag;
    }

    /**
     * @return bool
     */
    public function getQuiet()
    {
        return $this->quiet;
    }

    /**
     * @return bool
     */
    public function isQuiet()
    {
        return $this->getQuiet();
    }

}
<?php
/*
 *  $Id: d01c0f1426d0075ae04ff7c156452d08909e966a $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/git/GitBaseTask.php';

/**
 * Wrapper around git-tag
 *
 * @author Evan Kaufman <evan@digitalflophouse.com>
 * @author Victor Farazdagi <simple.square@gmail.com>
 * @version $Id: d01c0f1426d0075ae04ff7c156452d08909e966a $
 * @package phing.tasks.ext.git
 * @see VersionControl_Git
 * @since 2.4.5
 */
class GitTagTask extends GitBaseTask
{
    /**
     * Make unsigned, annotated tag object. See -a of git-tag
     * @var boolean
     */
    private $annotate = false;

    /**
     * Make GPG-signed tag. See -s of git-tag
     * @var boolean
     */
    private $sign = false;

    /**
     * Make GPG-signed tag, using given key. See -u of git-tag
     * @var string
     */
    private $keySign;

    /**
     * Replace existing tag with given name. See -f of git-tag
     * @var boolean
     */
    private $replace = false;

    /**
     * Delete existing tags with given names. See -d of git-tag
     * @var boolean
     */
    private $delete = false;

    /**
     * Verify gpg signature of given tag names. See -v of git-tag
     * @var boolean
     */
    private $verify = false;

    /**
     * List tags with names matching given pattern. See -l of git-tag
     * @var boolean
     */
    private $list = false;

    /**
     * <num> specifies how many lines from the annotation, if any, are printed
     * when using -l. See -n of git-tag
     * @var int
     */
    private $num;

    /**
     * Only list tags containing specified commit. See --contains of git-tag
     * @var string
     */
    private $contains;

    /**
     * Use given tag message. See -m of git-tag
     * @var string
     */
    private $message;

    /**
     * Take tag message from given file. See -F of git-tag
     * @var string
     */
    private $file;

    /**
     * <tagname> argument to git-tag
     * @var string
     */
    private $name;

    /**
     * <commit> argument to git-tag
     * @var string
     */
    private $commit;

    /**
     * <object> argument to git-tag
     * @var string
     */
    private $object;

    /**
     * <pattern> argument to git-tag
     * @var string
     */
    private $pattern;

    /**
     * Property name to set with output value from git-tag
     * @var string
     */
    private $outputProperty;

    /**
     * The main entry point for the task
     */
    public function main()
    {
        if (null === $this->getRepository()) {
            throw new BuildException('"repository" is required parameter');
        }

        $client = $this->getGitClient(false, $this->getRepository());
        $command = $client->getCommand('tag');
        $command
            ->setOption('a', $this->isAnnotate())
            ->setOption('s', $this->isSign())
            ->setOption('f', $this->isReplace())
            ->setOption('d', $this->isDelete())
            ->setOption('v', $this->isVerify())
            ->setOption('l', $this->isList());

        if (null !== $this->getKeySign()) {
            $command->setOption('u', $this->getKeySign());
        }

        if (null !== $this->getMessage()) {
            $command->setOption('m', $this->getMessage());
        }

        if (null !== $this->getFile()) {
            $command->setOption('F', $this->getFile());
        }

        // Use 'name' arg, if relevant
        if (null != $this->getName() && false == $this->isList()) {
            $command->addArgument($this->getName());
        }

        if (null !== $this->getKeySign() || $this->isAnnotate() || $this->isSign()) {
            // Require a tag message or file
            if (null === $this->getMessage() && null === $this->getFile()) {
                throw new BuildException('"message" or "file" required to make a tag');
            }
        }

        // Use 'commit' or 'object' args, if relevant
        if (null !== $this->getCommit()) {
            $command->addArgument($this->getCommit());
        } else {
            if (null !== $this->getObject()) {
                $command->addArgument($this->getObject());
            }
        }

        // Customize list (-l) options
        if ($this->isList()) {
            if (null !== $this->getContains()) {
                $command->setOption('contains', $this->getContains());
            }
            if (null !== $this->getPattern()) {
                $command->addArgument($this->getPattern());
            }
            if (null != $this->getNum()) {
                $command->setOption('n', $this->getNum());
            }
        }

        $this->log('git-tag command: ' . $command->createCommandString(), Project::MSG_INFO);

        try {
            $output = $command->execute();
        } catch (Exception $e) {
            $this->log($e->getMessage(), Project::MSG_ERR);
            throw new BuildException('Task execution failed. ' . $e->getMessage());
        }

        if (null !== $this->outputProperty) {
            $this->project->setProperty($this->outputProperty, $output);
        }

        $this->log(
            sprintf('git-tag: tags for "%s" repository', $this->getRepository()),
            Project::MSG_INFO
        );
        $this->log('git-tag output: ' . trim($output), Project::MSG_INFO);
    }

    /**
     * @param $flag
     */
    public function setAnnotate($flag)
    {
        $this->annotate = (bool) $flag;
    }

    /**
     * @return bool
     */
    public function getAnnotate()
    {
        return $this->annotate;
    }

    /**
     * @return bool
     */
    public function isAnnotate()
    {
        return $this->getAnnotate();
    }

    /**
     * @param $flag
     */
    public function setSign($flag)
    {
        $this->sign = (bool) $flag;
    }

    /**
     * @return bool
     */
    public function getSign()
    {
        return $this->sign;
    }

    /**
     * @return bool
     */
    public function isSign()
    {
        return $this->getSign();
    }

    /**
     * @param $keyId
     */
    public function setKeySign($keyId)
    {
        $this->keySign = $keyId;
    }

    /**
     * @return string
     */
    public function getKeySign()
    {
        return $this->keySign;
    }

    /**
     * @param $flag
     */
    public function setReplace($flag)
    {
        $this->replace = (bool) $flag;
    }

    /**
     * @return bool
     */
    public function getReplace()
    {
        return $this->replace;
    }

    /**
     * @return bool
     */
    public function isReplace()
    {
        return $this->getReplace();
    }

    /**
     * @param $flag
     */
    public function setForce($flag)
    {
        return $this->setReplace($flag);
    }

    /**
     * @param $flag
     */
    public function setDelete($flag)
    {
        $this->delete = (bool) $flag;
    }

    /**
     * @return bool
     */
    public function getDelete()
    {
        return $this->delete;
    }

    /**
     * @return bool
     */
    public function isDelete()
    {
        return $this->getDelete();
    }

    /**
     * @param $flag
     */
    public function setVerify($flag)
    {
        $this->verify = (bool) $flag;
    }

    /**
     * @return bool
     */
    public function getVerify()
    {
        return $this->verify;
    }

    /**
     * @return bool
     */
    public function isVerify()
    {
        return $this->getVerify();
    }

    /**
     * @param $flag
     */
    public function setList($flag)
    {
        $this->list = (bool) $flag;
    }

    /**
     * @return bool
     */
    public function getList()
    {
        return $this->list;
    }

    /**
     * @return bool
     */
    public function isList()
    {
        return $this->getList();
    }

    /**
     * @param $num
     */
    public function setNum($num)
    {
        $this->num = (int) $num;
    }

    /**
     * @return int
     */
    public function getNum()
    {
        return $this->num;
    }

    /**
     * @param $commit
     */
    public function setContains($commit)
    {
        $this->contains = $commit;
    }

    /**
     * @return string
     */
    public function getContains()
    {
        return $this->contains;
    }

    /**
     * @param $msg
     */
    public function setMessage($msg)
    {
        $this->message = $msg;
    }

    /**
     * @return string
     */
    public function getMessage()
    {
        return $this->message;
    }

    /**
     * @param $file
     */
    public function setFile($file)
    {
        $this->file = $file;
    }

    /**
     * @return string
     */
    public function getFile()
    {
        return $this->file;
    }

    /**
     * @param $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @param $commit
     */
    public function setCommit($commit)
    {
        $this->commit = $commit;
    }

    /**
     * @return string
     */
    public function getCommit()
    {
        return $this->commit;
    }

    /**
     * @param $object
     */
    public function setObject($object)
    {
        $this->object = $object;
    }

    /**
     * @return string
     */
    public function getObject()
    {
        return $this->object;
    }

    /**
     * @param $pattern
     */
    public function setPattern($pattern)
    {
        $this->pattern = $pattern;
    }

    /**
     * @return string
     */
    public function getPattern()
    {
        return $this->pattern;
    }

    /**
     * @param $prop
     */
    public function setOutputProperty($prop)
    {
        $this->outputProperty = $prop;
    }
}
<?php
/**
 * Copyright (c) 2012-2013, Laurent Laville <pear@laurent-laville.org>
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the authors nor the names of its contributors
 *       may be used to endorse or promote products derived from this software
 *       without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * PHP version 5
 *
 * @category   Tasks
 * @package    phing.tasks.ext
 * @version    $Id: 50d0d2d926e6a51ab78d6801407e90f020dcbad9 $
 * @author     Laurent Laville <pear@laurent-laville.org>
 * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
 * @link       https://github.com/llaville/phing-GrowlNotifyTask
 */

require_once 'phing/Task.php';

/**
 * Growl notification task for Phing, the PHP build tool.
 *
 * PHP version 5
 *
 * @category   Tasks
 * @package    phing.tasks.ext
 * @version    $Id: 50d0d2d926e6a51ab78d6801407e90f020dcbad9 $
 * @author     Laurent Laville <pear@laurent-laville.org>
 * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
 * @link       https://github.com/llaville/phing-GrowlNotifyTask
 */
class GrowlNotifyTask extends Task
{
    protected $growl;

    protected $name;
    protected $sticky;
    protected $message;
    protected $title;
    protected $notification;
    protected $appicon;
    protected $host;
    protected $password;
    protected $priority;
    protected $protocol;
    protected $icon;

    /**
     * Initializes task with default options
     *
     * @param Net_Growl $growl (optional) mock instance
     */
    public function __construct(Net_Growl $growl = null)
    {
        $this->growl = $growl;
    }

    /**
     * The init method check if Net_Growl is available
     * (exists and can be loaded)
     *
     * @return void
     * @throws BuildException
     */
    public function init()
    {
        $autoloader = 'Net/Growl/Autoload.php';

        if (!$handle = @fopen($autoloader, 'r', true)) {
            throw new BuildException(
                'The Growl Notify task requires the Net_Growl PEAR package.'
            );
        } else {
            fclose($handle);
            include_once $autoloader;
        }

        $this->setTaskName('GrowlNotify');
        $this->setName();
        $this->setSticky(false);
        $this->setMessage();
        $this->setTitle();
        $this->setNotification();
        $this->setAppicon();
        $this->setHost();
        $this->setPassword();
        $this->setPriority();
        $this->setProtocol();
        $this->setIcon();
    }

    /**
     * Defines the name of the application sending the notification
     *
     * @param string $name (optional) Name of the application
     *                     that appears in your Growl preferences
     *                     Default: "Growl for Phing"
     *
     * @return void
     * @throws BuildException
     */
    public function setName($name = '')
    {
        if ('' == $name) {
            $name = 'Growl for Phing';
        }

        if (!is_string($name)) {
            throw new BuildException(
                '"name" attribute is invalid.' .
                ' Expect to be a string, actual is ' . gettype($name)
            );
        }

        $this->name = $name;
    }

    /**
     * Indicates if the notification should be sticky
     *
     * @param bool $sticky (optional) Notification should be sticky
     *
     * @return void
     */
    public function setSticky($sticky = true)
    {
        $this->sticky = (bool) $sticky;
    }

    /**
     * The notification's text is required.
     * Use \n to specify a line break.
     *
     * @param string $message Notification's text
     *
     * @return void
     * @throws BuildException
     */
    public function setMessage($message = '')
    {
        if (!is_string($message)) {
            throw new BuildException(
                '"message" attribute is invalid.' .
                ' Expect to be a string, actual is ' . gettype($message)
            );
        }

        $this->message = $message;
    }

    /**
     * The notification's title.
     * Use \n to specify a line break.
     *
     * @param string $title (optional) Notification's title
     *                      Default: GrowlNotify
     *
     * @return void
     * @throws BuildException
     */
    public function setTitle($title = '')
    {
        if ('' == $title) {
            $title = 'GrowlNotify';
        }

        if (!is_string($title)) {
            throw new BuildException(
                '"title" attribute is invalid.' .
                ' Expect to be a string, actual is ' . gettype($title)
            );
        }

        $this->title = $title;
    }

    /**
     * The notification name/type
     *
     * @param string $notification Name/type
     *                             Default: "General Notification"
     *
     * @return void
     * @throws BuildException
     */
    public function setNotification($notification = '')
    {
        if ('' == $notification) {
            $notification = 'General Notification';
        }

        if (!is_string($notification)) {
            throw new BuildException(
                '"notification" attribute is invalid.' .
                ' Expect to be a string, actual is ' . gettype($notification)
            );
        }

        $this->notification = $notification;
    }

    /**
     * The icon of the application being registered.
     *
     * Must be a valid file type (png, jpg, gif, ico).
     * Can be any of the following:
     *  - absolute url (http://domain/image.png)
     *  - absolute file path (c:\temp\image.png)
     *  - relative file path (.\folder\image.png) (relative file paths must start
     *    with a dot and are relative to GrowlNotify's phing task location
     *
     * @param string $icon Icon of the application
     *
     * @return void
     * @throws BuildException
     */
    public function setAppicon($icon = '')
    {
        if (!is_string($icon)) {
            throw new BuildException(
                '"appicon" attribute is invalid.' .
                ' Expect to be a string, actual is ' . gettype($icon)
            );
        }

        // relative location
        if (strpos($icon, '..') === 0) {
            $icon = realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . $icon);

        } elseif (strpos($icon, '.') === 0) {
            $icon = dirname(__FILE__) . substr($icon, 1);
        }

        $this->appicon = $icon;
    }

    /**
     * The host address to send the notification to.
     *
     * If any value other than 'localhost' or '127.0.0.1' is provided, the host
     * is considered a remote host and the "pass" attribute must also be provided.
     * Default: 127.0.0.1
     *
     * @param string $host Remote host name/ip
     *                     Default: 127.0.0.1
     *
     * @return void
     * @throws BuildException
     */
    public function setHost($host = '127.0.0.1')
    {
        if (!is_string($host)) {
            throw new BuildException(
                '"host" attribute is invalid.' .
                ' Expect to be a string, actual is ' . gettype($host)
            );
        }

        $this->host = $host;
    }

    /**
     * The password required to send notifications.
     *
     * A password is required to send a request to a remote host. If host attribute
     * is specified and is any value other than 'localhost' or '127.0.0.1',
     * then "pass" attribute is also required.
     * Default: no password
     *
     * @param string $password Password to send request to a remote host
     *
     * @return void
     * @throws BuildException
     */
    public function setPassword($password = '')
    {
        if (!is_string($password)) {
            throw new BuildException(
                '"password" attribute is invalid.' .
                ' Expect to be a string, actual is ' . gettype($password)
            );
        }

        $this->password = $password;
    }

    /**
     * The notification priority.
     *
     * Valid values are : low, moderate, normal, high, emergency
     * Default: normal
     *
     * @param string $priority Notification priority
     *                         Default: normal
     *
     * @return void
     * @throws BuildException
     */
    public function setPriority($priority = '')
    {
        if ('' == $priority) {
            $priority = 'normal';
        }

        switch ($priority) {
            case 'low' :
                $priority = Net_Growl::PRIORITY_LOW;
                break;
            case 'moderate' :
                $priority = Net_Growl::PRIORITY_MODERATE;
                break;
            case 'normal' :
                $priority = Net_Growl::PRIORITY_NORMAL;
                break;
            case 'high' :
                $priority = Net_Growl::PRIORITY_HIGH;
                break;
            case 'emergency' :
                $priority = Net_Growl::PRIORITY_EMERGENCY;
                break;
            default :
                throw new BuildException(
                    '"priority" attribute is invalid.'
                );
        }

        $this->priority = $priority;
    }

    /**
     * The protocol (and port) to send the notification to.
     *
     * With TCP (GNTP) protocol, port is always 23053
     * With UDP protocol, port is always 9887
     * Default: 23053
     *
     * @param string $protocol Protocol to use to send request to remote host
     *                         Default: gntp
     *
     * @return void
     * @throws BuildException
     */
    public function setProtocol($protocol = '')
    {
        if ('' == $protocol) {
            $protocol = 'gntp';
        }

        switch ($protocol) {
            case 'udp' :
            case 'gntp' :
                break;
            default :
                throw new BuildException(
                    '"protocol" attribute is invalid.' .
                    ' Expect to be either udp or gntp.'
                );
        }

        $this->protocol = $protocol;
    }

    /**
     * The icon to show for the notification.
     *
     * Must be a valid file type (png, jpg, gif, ico).
     * Can be any of the following:
     *  - absolute url (http://domain/image.png)
     *  - absolute file path (c:\temp\image.png)
     *  - relative file path (.\folder\image.png) (relative file paths must start
     *    with a dot and are relative to GrowlNotify's phing task location
     *
     * @param string $icon Icon of the message
     *
     * @return void
     * @throws BuildException
     */
    public function setIcon($icon = '')
    {
        if (!is_string($icon)) {
            throw new BuildException(
                '"icon" attribute is invalid.' .
                ' Expect to be a string, actual is ' . gettype($icon)
            );
        }

        // relative location
        if (strpos($icon, '..') === 0) {
            $icon = realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . $icon);

        } elseif (strpos($icon, '.') === 0) {
            $icon = dirname(__FILE__) . substr($icon, 1);
        }

        $this->icon = $icon;
    }

    /**
     * The main entry point method
     *
     * @return void
     * @throws BuildException
     */
    public function main()
    {
        if (empty($this->message)) {
            throw new BuildException(
                '"message" attribute cannot be empty'
            );
        }

        $notifications = array(
            $this->notification
        );
        $options = array(
            'host' => $this->host,
            'protocol' => $this->protocol,
        );
        if (!empty($this->appicon)) {
            $options['AppIcon'] = $this->appicon;
        }

        try {
            if ($this->growl instanceof Net_Growl) {
                $growl = $this->growl;
            } else {
                $growl = Net_Growl::singleton(
                    $this->name,
                    $notifications,
                    $this->password,
                    $options
                );
            }
            $response = $growl->register();

            if ($this->protocol == 'gntp') {
                if ($response->getStatus() != 'OK') {
                    throw new BuildException(
                        'Growl Error ' . $response->getErrorCode() .
                        ' - ' . $response->getErrorDescription()
                    );
                }
            }
            $this->log(
                'Application ' . $this->name . ' registered',
                Project::MSG_VERBOSE
            );

            $logRequest = array(
                'Application-Name' => $this->name,
                'Application-Icon' => $this->appicon,
                'Notification-Name' => $this->notification,
                'Notification-Title' => $this->title,
                'Notification-Text' => $this->message,
                'Notification-Priority' => $this->priority,
                'Notification-Icon' => $this->icon,
                'Notification-Sticky' => $this->sticky,
            );
            foreach ($logRequest as $key => $value) {
                $this->log($key . ': ' . $value, Project::MSG_DEBUG);

            }

            $options = array(
                'sticky' => $this->sticky,
                'priority' => $this->priority,
                'icon' => $this->icon,
            );
            $response = $growl->publish(
                $this->notification,
                $this->title,
                $this->message,
                $options
            );

            if ($this->protocol == 'gntp') {
                if ($response->getStatus() != 'OK') {
                    throw new BuildException(
                        'Growl Error ' . $response->getErrorCode() .
                        ' - ' . $response->getErrorDescription()
                    );
                }
            }
            $this->log('Notification was sent to remote host ' . $this->host);

        } catch (Net_Growl_Exception $e) {
            throw new BuildException(
                'Growl Exception : ' . $e->getMessage()
            );
        }
    }

}
<?php
/**
 * Utilise Mercurial from within Phing.
 *
 * PHP Version 5.4
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     https://github.com/kenguest/Phing-HG
 */

/**
 * Pull in Base class.
 */
require_once 'phing/tasks/ext/hg/HgBaseTask.php';

/**
 * Integration/Wrapper for hg add
 *
 * @category Tasks
 * @package  phing.tasks.ext.hg
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     HgAddTask.php
 */
class HgAddTask extends HgBaseTask
{
    /**
     * Linked filesets
     *
     * @var string
     */
    protected $filesets = array();
    /**
     * Array of files to ignore
     *
     * @var string[]
     */
    protected $ignoreFile = array();

    /**
     * Adds a fileset of files to add to the repository.
     *
     * @param FileSet $fileset Set of files to add to the repository.
     *
     * @return void
     */
    public function addFileSet(FileSet $fileset)
    {
        $this->filesets[] = $fileset;
    }

    /**
     * The main entry point method.
     *
     * @throws BuildException
     * @return void
     */
    public function main()
    {
        $filesAdded = false;
        $clone = $this->getFactoryInstance('add');
        $clone->setQuiet($this->getQuiet());

        $cwd = getcwd();
        $project = $this->getProject();
        if ($this->repository === '') {
            $dir = $project->getProperty('application.startdir');
        } else {
            $dir = $this->repository;
        }

        if (!file_exists($dir)) {
            throw new BuildException("\"$dir\" does not exist.");
        } elseif (!is_dir($dir)) {
            throw new BuildException("\"$dir\" is not a directory.");
        }

        chdir($dir);

        if (file_exists('.hgignore')) {
            $this->loadIgnoreFile();
        }
        if (count($this->filesets)) {
            $this->log('filesets set', Project::MSG_DEBUG);
            /**
             * $fs is a FileSet
             *
             * @var $fs FileSet
             */
            foreach ($this->filesets as $fs) {
                $ds = $fs->getDirectoryScanner($project);
                $fromDir = $fs->getDir($project);
                if ($fromDir->getName() === '.') {
                    $statusClone = $this->getFactoryInstance('status');
                    $statusClone->setUnknown(true);
                    $statusClone->setNoStatus(true);
                    $statusClone->setRepository($this->getRepository());
                    $statusOut = $statusClone->execute();
                    if ($statusOut !== '') {
                        $files = explode(PHP_EOL, $statusOut);
                        foreach ($files as $file) {
                            if ($file != '') {
                                $clone->addFile($file);
                                $filesAdded = true;
                            }
                        }
                    }
                }
            }
        }

        if ($filesAdded) {
            try {
                $this->log("Executing: " . $clone->asString(), Project::MSG_INFO);
                $output = $clone->execute();
                if ($output !== '') {
                    $this->log($output);
                }
            } catch(Exception $ex) {
                $msg = $ex->getMessage();
                $this->log("Exception: $msg", Project::MSG_INFO);
                $p = strpos($msg, 'hg returned:');
                if ($p !== false) {
                    $msg = substr($msg, $p + 13);
                }
                chdir($cwd);
                throw new BuildException($msg);
            }
        }
        chdir($cwd);
    }

    /**
     * Load .hgignore file.
     *
     * @return void
     */
    public function loadIgnoreFile()
    {
        $ignores = array();
        $lines = file('.hgignore');
        foreach ($lines as $line) {
            $nline =  trim($line);
            $nline = preg_replace('/\/\*$/', '/', $nline);
            $ignores[] = $nline;
        }
        $this->ignoreFile = $ignores;
    }

    /**
     * Determine if a file is to be ignored.
     *
     * @param string $file filename
     *
     * @return bool
     */
    public function fileIsIgnored($file)
    {
        $line = $this->ignoreFile[0];
        $mode = 'regexp';
        $ignored = false;
        if (preg_match('#^syntax\s*:\s*(glob|regexp)$#', $line, $matches)
            || $matches[1] === 'glob'
        ) {
            $mode = 'glob';
        }
        if ($mode === 'glob') {
            $ignored = $this->ignoredByGlob($file);
        } elseif ($mode === 'regexp') {
            $ignored = $this->ignoredByRegex($file);
        }
        return $ignored;
    }

    /**
     * Determine if file is ignored by glob pattern.
     *
     * @param string $file filename
     *
     * @return bool
     */
    public function ignoredByGlob($file)
    {
        $lfile = $file;
        if (strpos($lfile, './') === 0) {
            $lfile = substr($lfile, 2);
        }
        foreach ($this->ignoreFile as $line) {
            if (strpos($lfile, $line) === 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * Is file ignored by regex?
     *
     * @param string $file Filename
     *
     * @return bool
     */
    public function ignoredByRegex($file)
    {
        return true;
    }
}
<?php
/**
 * Utilise Mercurial from within Phing.
 *
 * PHP Version 5.4
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     https://github.com/kenguest/Phing-HG
 */

/**
 * Pull in Base class.
 */
require_once 'phing/tasks/ext/hg/HgBaseTask.php';

/**
 * Integration/Wrapper for hg archive
 *
 * @category Tasks
 * @package  phing.tasks.ext.hg
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     HgArchiveTask.php
 */
class HgArchiveTask extends HgBaseTask
{
    /**
     * Which revision to archive.
     *
     * @var string
     */
    protected $revision = '';

    /**
     * Name of destination archive file.
     *
     * @var string
     */
    protected $destination = null;

    /**
     * Set revision attribute
     *
     * @param string $revision Revision
     *
     * @return void
     */
    public function setRevision($revision)
    {
        $this->revision = $revision;
    }

    /**
     * Set Destination attribute
     *
     * @param string $destination Destination filename
     *
     * @return void
     */
    public function setDestination($destination)
    {
        $this->destination = $destination;
    }

    /**
     * The main entry point for the task.
     *
     * @return void
     */
    public function main()
    {
        $clone = $this->getFactoryInstance('archive');
        if ($this->revision !== '') {
            $clone->setRev($this->revision);
        }

        if ($this->destination === null) {
            throw new BuildException("Destination must be set.");
        }
        $clone->setDestination($this->destination);

        try {
            $this->log("Executing: " . $clone->asString(), Project::MSG_INFO);
            $output = $clone->execute();
            if ($output !== '') {
                $this->log(PHP_EOL . $output);
            }
        } catch(Exception $ex) {
            $msg = $ex->getMessage();
            $p = strpos($msg, 'hg returned:');
            if ($p !== false) {
                $msg = substr($msg, $p + 13);
            }
            throw new BuildException($msg);
        }
    }
}
<?php
/**
 * Utilise Mercurial from within Phing.
 *
 * PHP Version 5.4
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     https://github.com/kenguest/Phing-HG
 */

/**
 * Base task for integrating phing and mercurial.
 *
 * @category Tasks
 * @package  phing.tasks.ext.hg
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     HgBaseTask.php
 */
abstract class HgBaseTask extends Task
{
    /**
     * Insecure argument
     *
     * @var string
     */
    protected $insecure = '';

    /**
     * Repository directory
     *
     * @var string
     */
    protected $repository = '';

    /**
     * Whether to be quiet... --quiet argument.
     *
     * @var bool
     */
    protected $quiet = false;

    /**
     * Username.
     *
     * @var string
     */
    protected $user = '';

    static $factory = null;
    /**
     * Set repository attribute
     *
     * @param string $repository Repository
     *
     * @return void
     */
    public function setRepository($repository)
    {
        $this->repository = $repository;
    }


    /**
     * Set the quiet attribute --quiet
     *
     * @param string $quiet yes|no|true|false|1|0
     *
     * @return void
     */
    public function setQuiet($quiet)
    {
        $this->quiet = StringHelper::booleanValue($quiet);
    }

    /**
     * Get the quiet attribute value.
     *
     * @return bool
     */
    public function getQuiet()
    {
        return $this->quiet;
    }

    /**
     * Get Repository attribute/directory.
     *
     * @return string
     */
    public function getRepository()
    {
        return $this->repository;
    }

    /**
     * Set insecure attribute
     *
     * @param string $insecure 'yes', etc.
     *
     * @return void
     */
    public function setInsecure($insecure)
    {
        $this->insecure = StringHelper::booleanValue($insecure);
    }

    /**
     * Get 'insecure' attribute value. (--insecure or null)
     *
     * @return string
     */
    public function getInsecure()
    {
        return $this->insecure;
    }

    /**
     * Set user attribute
     *
     * @param string $user username/email address.
     *
     * @return void
     */
    public function setUser($user)
    {
        $this->user = $user;
    }

    /**
     * Get username attribute.
     *
     * @return string
     */
    public function getUser()
    {
        return $this->user;
    }

    /**
     * Check provided repository directory actually is an existing directory.
     *
     * @param string $dir Repository directory
     *
     * @return bool
     * @throws BuildException
     */
    public function checkRepositoryIsDirAndExists($dir)
    {
        if (file_exists($dir)) {
            if (!is_dir($dir)) {
                throw new BuildException("Repository '$dir' is not a directory.");
            }
        } else {
            throw new BuildException("Repository directory '$dir' does not exist.");
        }
        return true;
    }

    /**
     * Initialise the task.
     *
     * @return void
     */
    public function init()
    {
        if (version_compare(PHP_VERSION, '5.4', "<")) {
            throw new BuildException('This task requires PHP 5.4+');
        } else {
            /**
            * Depending on composer for pulling in siad007's VersionControl_HG.
            */
            @include_once 'vendor/autoload.php';
        }
    }

    public function getFactoryInstance($command, $options = array())
    {
        $vchq = '\\Siad007\\VersionControl\\HG\\Factory';
        self::$factory = call_user_func_array(
            array($vchq, 'getInstance'),
            array($command, $options)
        );
        return self::$factory;
    }

}
<?php
/**
 * Utilise Mercurial from within Phing.
 *
 * PHP Version 5.4
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     https://github.com/kenguest/Phing-HG
 */

/**
 * Pull in Base class.
 */
require_once 'phing/tasks/ext/hg/HgBaseTask.php';

/**
 * Integration/Wrapper for hg clone
 *
 * @category Tasks
 * @package  phing.tasks.ext.hg
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     HgCloneTask.php
 */
class HgCloneTask extends HgBaseTask
{
    /**
     * Path to target directory
     *
     * @var string
     */
    protected $targetPath = '';

    /**
     * Set path to source repo
     *
     * @param string $targetPath Path to repository used as source
     *
     * @return void
     */
    public function setTargetPath($targetPath)
    {
        $this->targetPath = $targetPath;
    }

    /**
     * Get path to the target directory/repo.
     *
     * @return string
     */
    public function getTargetPath()
    {
        return $this->targetPath;
    }

    /**
     * The main entry point.
     *
     * @return void
     * @throws BuildException
     */
    public function main()
    {
        $clone = $this->getFactoryInstance('clone');
        $repository = $this->getRepository();
        if ($repository === '') {
            throw new BuildException('"repository" is a required parameter');
        }
        $target = $this->getTargetPath();
        if ($target === '') {
            throw new BuildException('"targetPath" is a required parameter');
        }
        // Is target path empty?
        if (file_exists($target)) {
            $files = scandir($target);
            if (is_array($files) && count($files) > 2) {
                throw new BuildException("Directory \"$target\" is not empty");
            }
            if (!is_dir($target)) {
                throw new BuildException("\"$target\" is not a directory");
            }
        }
        $msg = sprintf('hg cloning %s to %s', $repository, $target);
        $this->log($msg, Project::MSG_INFO);
        $clone->setSource($repository);
        $clone->setDestination($target);
        $clone->setInsecure($this->getInsecure());
        $clone->setQuiet($this->getQuiet());
        try {
            $this->log("Executing: " . $clone->asString(), Project::MSG_INFO);
            $output = $clone->execute();
            if ($output !== '') {
                $this->log($output);
            }
        } catch(Exception $ex) {
            $msg = $ex->getMessage();
            $p = strpos($msg, 'hg returned:');
            if ($p !== false) {
                $msg = substr($msg, $p + 13);
            }
            throw new BuildException($msg);
        }
    }
}
<?php
/**
 * Utilise Mercurial from within Phing.
 *
 * PHP Version 5.4
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     https://github.com/kenguest/Phing-HG
 */

/**
 * Pull in Base class.
 */
require_once 'phing/tasks/ext/hg/HgBaseTask.php';

/**
 * Integration/Wrapper for hg commit
 *
 * @category Tasks
 * @package  phing.tasks.ext.hg
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     HgCommitTask.php
 */
class HgCommitTask extends HgBaseTask
{
    /**
     * Message to be recorded against commit.
     *
     * @var string
     */
    protected $message = '';

    /**
     * Set message to be used.
     *
     * @param string $message Message to use
     *
     * @return void
     */
    public function setMessage($message)
    {
        $this->message = $message;
    }

    /**
     * Get message to apply for the commit.
     *
     * @return string
     */
    public function getMessage()
    {
        return $this->message;
    }

    /**
     * The main entry point method.
     *
     * @throws BuildException If message is not set
     * @throws BuildException If error occurs during commit
     * @return void
     */
    public function main()
    {

        $message = $this->getMessage();
        if ($message === '') {
            throw new BuildException('"message" is a required parameter');
        }

        $user = $this->getUser();

        $clone = $this->getFactoryInstance('commit');
        $msg = sprintf("Commit: '%s'", $message);
        $this->log($msg, Project::MSG_INFO);
        $clone->setQuiet($this->getQuiet());
        $clone->setMessage($message);

        if (trim($user) === "") {
            throw new BuildException('"user" parameter can not be set to ""');
        }
        if ($user !== null) {
            $clone->setUser($user);
            $this->log("Commit: user = '$user'", Project::MSG_VERBOSE);
        }

        if ($this->repository === '') {
            $project = $this->getProject();
            $dir = $project->getProperty('application.startdir');
        } else {
            $dir = $this->repository;
        }
        $this->log('DIR:' . $dir, Project::MSG_INFO);
        $this->log('REPO: ' . $this->repository, Project::MSG_INFO);
        $cwd = getcwd();
        chdir($dir);

        try {
            $this->log("Executing: " . $clone->asString(), Project::MSG_INFO);
            $output = $clone->execute();
            if ($output !== '') {
                $this->log($output);
            }
        } catch(Exception $ex) {
            $msg = $ex->getMessage();
            $this->log("Exception: $msg", Project::MSG_INFO);
            $p = strpos($msg, 'hg returned:');
            if ($p !== false) {
                $msg = substr($msg, $p + 13);
            }
            chdir($cwd);
            throw new BuildException($msg);
        }
        chdir($cwd);
    }
}

<?php
/**
 * Utilise Mercurial from within Phing.
 *
 * PHP Version 5.4
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     https://github.com/kenguest/Phing-HG
 */

/**
 * Pull in Base class.
 */
require_once 'phing/tasks/ext/hg/HgBaseTask.php';

/**
 * Integration/Wrapper for hg init
 *
 * @category Tasks
 * @package  phing.tasks.ext.hg
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     HgInitTask.php
 */
class HgInitTask extends HgBaseTask
{
    /**
     * Path to target directory
     *
     * @var string
     */
    protected $targetPath;

    /**
     * Set path to source repo
     *
     * @param string $targetPath Path to repository used as source
     *
     * @return void
     */
    public function setTargetPath($targetPath)
    {
        $this->targetPath = $targetPath;
    }

    /**
     * Main entry point for this task.
     *
     * @return void
     */
    public function main()
    {
        $clone = $this->getFactoryInstance('init');
        $this->log('Initializing', Project::MSG_INFO);
        $clone->setQuiet($this->getQuiet());
        $clone->setInsecure($this->getInsecure());
        $cwd = getcwd();
        if ($this->repository === '') {
            $project = $this->getProject();
            $dir = $project->getProperty('application.startdir');
        } else {
            $dir = $this->repository;
        }
        if (!is_dir($dir)) {
            throw new BuildException("$dir is not a directory.");
        }
        chdir($dir);
        try {
            $this->log("Executing: " . $clone->asString(), Project::MSG_INFO);
            $output = $clone->execute();
            if ($output !== '') {
                $this->log($output);
            }
        } catch(Exception $ex) {
            $msg = $ex->getMessage();
            $p = strpos($msg, 'hg returned:');
            if ($p !== false) {
                $msg = substr($msg, $p + 13);
            }
            chdir($cwd);
            throw new BuildException($msg);
        }
        chdir($cwd);
    }
}

<?php
/**
 * Utilise Mercurial from within Phing.
 *
 * PHP Version 5.4
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     https://github.com/kenguest/Phing-HG
 */

/**
 * Pull in Base class.
 */
require_once 'phing/tasks/ext/hg/HgBaseTask.php';

/**
 * Integration/Wrapper for hg log
 *
 * @category Tasks
 * @package  phing.tasks.ext.hg
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     HgLogTask.php
 */
class HgLogTask extends HgBaseTask
{
    /**
     * Maximum number of changes to get. See --limit
     *
     * @var int
     */
    protected $maxCount = null;

    /**
     * Commit format/template. See --template
     *
     * @var string
     */
    protected $format = null;

    /**
     * Revision
     *
     * @var string
     */
    protected $revision = '';

    /**
     * Propery name to set the output to.
     *
     * @var string
     */
    protected $outputProperty = null;

    /**
     * Set maximum number of changes to get.
     *
     * @param int $count Maximum number of log entries to retrieve.
     *
     * @return void
     */
    public function setMaxcount($count)
    {
        $this->maxCount = $count;
    }

    /**
     * Retrieve max count of commits to limit to.
     *
     * @return int
     */
    public function getMaxcount()
    {
        return $this->maxCount;
    }

    /**
     * Template/log format.
     *
     * @param string $format Log format
     *
     * @return string
     */
    public function setFormat($format)
    {
        $this->format = $format;
    }

    /**
     * Get the log format/template
     *
     * @return string
     */
    public function getFormat()
    {
        return $this->format;
    }

    /**
     * Property to assign output to.
     *
     * @param string $property name of property to assign output to.
     *
     * @return void
     */
    public function setOutputProperty($property)
    {
        $this->outputProperty = $property;
    }

    /**
     * Set revision attribute
     *
     * @param string $revision Revision
     *
     * @return void
     */
    public function setRevision($revision)
    {
        $this->revision = $revision;
    }

    /**
     * Main entry point for this task
     *
     * @return void
     */
    public function main()
    {
        $clone = $this->getFactoryInstance('log');

        if ($this->repository === '') {
            $project = $this->getProject();
            $dir = $project->getProperty('application.startdir');
        } else {
            $dir = $this->repository;
        }
        $clone->setCwd($dir);

        if ($this->maxCount !== null) {
            $max = filter_var($this->maxCount, FILTER_VALIDATE_INT);
            if ($max) {
                $max = (int) $this->maxCount;
            }
            if (!$max || (int) $this->maxCount <= 0) {
                throw new BuildException("maxcount should be a positive integer.");
            }
            $clone->setLimit('' . $this->maxCount);
        }

        if ($this->format !== null) {
            $clone->setTemplate($this->format);
        }

        if ($this->revision !== '') {
            $clone->setRev($this->revision);
        }

        try {
            $this->log("Executing: " . $clone->asString(), Project::MSG_INFO);
            $output = $clone->execute();
            if ($this->outputProperty !== null) {
                $this->project->setProperty($this->outputProperty, $output);
            } else {
                if ($output !== '') {
                    $this->log(PHP_EOL . $output);
                }
            }
        } catch(Exception $ex) {
            $msg = $ex->getMessage();
            $p = strpos($msg, 'hg returned:');
            if ($p !== false) {
                $msg = substr($msg, $p + 13);
            }
            throw new BuildException($msg);
        }
    }
}
<?php
/**
 * Utilise Mercurial from within Phing.
 *
 * PHP Version 5.4
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     https://github.com/kenguest/Phing-HG
 */

/**
 * Pull in Base class.
 */
require_once 'phing/tasks/ext/hg/HgBaseTask.php';

/**
 * Integration/Wrapper for hg update
 *
 * @category Tasks
 * @package  phing.tasks.ext.hg
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     HgPullTask.php
 */
class HgPullTask extends HgBaseTask
{
    /**
     * Path to target directory
     *
     * @var string
     */
    protected $targetPath;

    /**
     * Set path to source repo
     *
     * @param string $targetPath Path to repository used as source
     *
     * @return void
     */
    public function setTargetPath($targetPath)
    {
        $this->targetPath = $targetPath;
    }

    /**
     * The main entry point method.
     *
     * @throws BuildException
     * @return void
     */
    public function main()
    {
        $clone = $this->getFactoryInstance('pull');
        $clone->setInsecure($this->getInsecure());
        $clone->setQuiet($this->getQuiet());

        $cwd = getcwd();

        if ($this->repository === '') {
            $project = $this->getProject();
            $dir = $project->getProperty('application.startdir');
        } else {
            $dir = $this->repository;
        }
        $this->checkRepositoryIsDirAndExists($dir);
        chdir($dir);

        try {
            $this->log("Executing: " . $clone->asString(), Project::MSG_INFO);
            $output = $clone->execute();
            if ($output !== '') {
                $this->log($output);
            }
        } catch(Exception $ex) {
            $msg = $ex->getMessage();
            $p = strpos($msg, 'hg returned:');
            if ($p !== false) {
                $msg = substr($msg, $p + 13);
            }
            chdir($cwd);
            throw new BuildException($msg);
        }
        chdir($cwd);
    }
}

<?php
/**
 * Utilise Mercurial from within Phing.
 *
 * PHP Version 5.4
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     https://github.com/kenguest/Phing-HG
 */

/**
 * Pull in Base class.
 */
require_once 'phing/tasks/ext/hg/HgBaseTask.php';

/**
 * Integration/Wrapper for hg push
 *
 * @category Tasks
 * @package  phing.tasks.ext.hg
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     HgPushTask.php
 */
class HgPushTask extends HgBaseTask
{
    /**
     * Whether the task should halt if an error occurs.
     *
     * @var bool
     */
    protected $haltonerror = false;

    /**
     * Set haltonerror attribute.
     *
     * @param string $halt 'yes', or '1' to halt.
     *
     * @return void
     */
    public function setHaltonerror($halt)
    {
        $this->haltonerror = StringHelper::booleanValue($halt);
    }

    /**
     * Return haltonerror value.
     *
     * @return bool
     */
    public function getHaltonerror()
    {
        return $this->haltonerror;
    }

    /**
     * The main entry point method.
     *
     * @throws BuildException
     * @return void
     */
    public function main()
    {
        $clone = $this->getFactoryInstance('push');
        $this->log('Pushing...', Project::MSG_INFO);
        $clone->setInsecure($this->getInsecure());
        $clone->setQuiet($this->getQuiet());
        if ($this->repository === '') {
            $project = $this->getProject();
            $dir = $project->getProperty('application.startdir');
        } else {
            $dir = $this->repository;
        }
        $cwd = getcwd();
        $this->checkRepositoryIsDirAndExists($dir);
        chdir($dir);
        try {
            $this->log("Executing: " . $clone->asString(), Project::MSG_INFO);
            $output = $clone->execute();
            if ($output !== '') {
                $this->log($output);
            }
        } catch(Exception $ex) {
            $msg = $ex->getMessage();
            $p = strpos($msg, 'hg returned:');
            if ($p !== false) {
                $msg = substr($msg, $p + 13);
            }
            chdir($cwd);
            if ($this->haltonerror) {
                throw new BuildException($msg);
            }
            $this->log($msg, Project::MSG_ERR);
        }
        chdir($cwd);
    }
}
<?php
/**
 * Utilise Mercurial from within Phing.
 *
 * PHP Version 5.4
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     https://github.com/kenguest/Phing-HG
 */

/**
 * Pull in Base class.
 */
require_once 'phing/tasks/ext/hg/HgBaseTask.php';

/**
 * Integration/Wrapper for hg revert
 *
 * @category Tasks
 * @package  phing.tasks.ext.hg
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     HgRevertTask.php
 */
class HgRevertTask extends HgBaseTask
{
    /**
     * All
     *
     * @var bool
     */
    protected $all = false;

    /**
     * Name of file to be reverted.
     *
     * @var string
     */
    protected $file = null;

    /**
     * Revision
     *
     * @var string
     */
    protected $revision = '';

    /**
     * Set whether all files are to be reverted.
     *
     * @param string $value Jenkins style boolean value
     *
     * @return void
     */
    public function setAll($value)
    {
        $this->all = StringHelper::booleanValue($value);
    }

    /**
     * Set filename to be reverted.
     *
     * @param string $file Filename
     *
     * @return void
     */
    public function setFile($file)
    {
        $this->file = $file;
    }

    /**
     * Get filename to be reverted.
     *
     * @return string
     */
    public function getFile()
    {
        return $this->file;
    }

    /**
     * Set revision attribute
     *
     * @param string $revision Revision
     *
     * @return void
     */
    public function setRevision($revision)
    {
        $this->revision = $revision;
    }

    /**
     * The main entry point method.
     *
     * @throws BuildException
     * @return void
     */
    public function main()
    {
        $clone = $this->getFactoryInstance('revert');
        $clone->setQuiet($this->getQuiet());
        $clone->setAll($this->all);
        if ($this->repository === '') {
            $project = $this->getProject();
            $dir = $project->getProperty('application.startdir');
        } else {
            $dir = $this->repository;
        }
        $cwd = getcwd();
        $this->checkRepositoryIsDirAndExists($dir);
        chdir($dir);
        if ($this->revision !== '') {
            $clone->setRev($this->revision);
        }
        if ($this->file !== null) {
            $clone->addName($this->file);
        }

        try {
            $this->log("Executing: " . $clone->asString(), Project::MSG_INFO);
            $output = $clone->execute();
            if ($output !== '') {
                $this->log(PHP_EOL . $output);
            }
        } catch(Exception $ex) {
            $msg = $ex->getMessage();
            $p = strpos($msg, 'hg returned:');
            if ($p !== false) {
                $msg = substr($msg, $p + 13);
            }
            throw new BuildException($msg);
        }
    }
}
<?php
/**
 * Utilise Mercurial from within Phing.
 *
 * PHP Version 5.4
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     https://github.com/kenguest/Phing-HG
 */

/**
 * Pull in Base class.
 */
require_once 'phing/tasks/ext/hg/HgBaseTask.php';

/**
 * Integration/Wrapper for hg tag
 *
 * @category Tasks
 * @package  phing.tasks.ext.hg
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     HgTagTask.php
 */
class HgTagTask extends HgBaseTask
{
    /**
     * Message to be recorded against tagging.
     *
     * @var string
     */
    protected $message = '';

    /**
     * Tag to assign/create.
     *
     * @var string
     */
    protected $name = '';

    /**
     * Revision
     *
     * @var string
     */
    protected $revision = '';

    /**
     * Set the name argument
     *
     * @param string $name Name
     *
     * @return void
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Get the name of the tag to be used.
     *
     * @return void
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set message to be used.
     *
     * @param string $message Message to use
     *
     * @return void
     */
    public function setMessage($message)
    {
        $this->message = $message;
    }

    /**
     * Get message to apply for the commit.
     *
     * @return string
     */
    public function getMessage()
    {
        return $this->message;
    }

    /**
     * Set revision attribute
     *
     * @param string $revision Revision
     *
     * @return void
     */
    public function setRevision($revision)
    {
        $this->revision = $revision;
    }

    /**
     * The main entry point method.
     *
     * @return void
     */
    public function main()
    {
        $clone = $this->getFactoryInstance('tag');
        $cwd = getcwd();

        if ($this->name === '') {
            throw new BuildException("Tag name must be set.");
        }
        if ($this->repository === '') {
            $prog = $this->getProject();
            $dir = $prog->getProperty('application.startdir');
        } else {
            $dir = $this->repository;
        }

        if ($this->revision !== '') {
            $clone->setRev($this->revision);
        }
        if ($this->user !== null) {
            $clone->setUser($this->user);
        }
        $message = $this->getMessage();
        $clone->setMessage($message);
        $name = $this->getName();
        if ($name == '') {
            throw new BuildException("Name attribute must be set.");
        }
        $clone->addName($name);

        $this->checkRepositoryIsDirAndExists($dir);
        chdir($dir);

        try {
            $this->log("Executing: " . $clone, Project::MSG_INFO);
            $output = $clone->execute();
            if ($output !== '') {
                $this->log($output);
            }
        } catch(Exception $ex) {
            $msg = $ex->getMessage();
            $p = strpos($msg, 'hg returned:');
            if ($p !== false) {
                $msg = substr($msg, $p + 13);
            }
            chdir($cwd);
            throw new BuildException($msg);
        }
        chdir($cwd);
    }
}
<?php
/**
 * Utilise Mercurial from within Phing.
 *
 * PHP Version 5.4
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     https://github.com/kenguest/Phing-HG
 */

/**
 * Pull in Base class.
 */
require_once 'phing/tasks/ext/hg/HgBaseTask.php';

/**
 * Integration/Wrapper for hg update
 *
 * @category Tasks
 * @package  phing.tasks.ext.hg
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     HgUpdateTask.php
 */
class HgUpdateTask extends HgBaseTask
{
    /**
     * Branch argument
     *
     * Defaults to 'default'
     *
     * @var string
     */
    protected $branch = 'default';

    /**
     * Clean argument
     *
     * @var bool
     */
    protected $clean = false;

    /**
     * Set 'clean' attribute.
     *
     * @param string $value Clean attribute value
     *
     * @return void
     */
    public function setClean($value)
    {
        $this->clean = StringHelper::booleanValue($value);
    }

    /**
     * Get 'clean' attribute.
     *
     * @return bool
     */
    public function getClean()
    {
        return $this->clean;
    }

    /**
     * Set branch attribute
     *
     * @param string $value Branch name
     *
     * @return void
     */
    public function setBranch($value)
    {
        $this->branch = $value;
    }

    /**
     * Get branch attribute
     *
     * @return string
     */
    public function getBranch()
    {
        return $this->branch;
    }

    /**
     * The main entry point method.
     *
     * @throws BuildException
     * @return void
     */
    public function main()
    {
        $pull = $this->getFactoryInstance('update');
        try {
            $pull->setBranch($this->getBranch());
        } catch (Exception $ex) {
            $this->log("Caught: " . $ex->getMessage(), Project::MSG_DEBUG);
        }
        $pull->setClean($this->getClean());
        $pull->setQuiet($this->getQuiet());

        $cwd = getcwd();

        if ($this->repository === '') {
            $prog = $this->getProject();
            $dir = $prog->getProperty('application.startdir');
        } else {
            $dir = $this->repository;
        }

        $this->checkRepositoryIsDirAndExists($dir);
        chdir($dir);
        try {
            $this->log("Executing: " . $pull->asString(), Project::MSG_INFO);
            $output = $pull->execute();
            if ($output !== '') {
                $this->log($output);
            }
        } catch(Exception $ex) {
            $msg = $ex->getMessage();
            $p = strpos($msg, 'hg returned:');
            if ($p !== false) {
                $msg = substr($msg, $p + 13);
            }
            chdir($cwd);
            throw new BuildException($msg);
        }
        chdir($cwd);
    }
}

<?php
/*
 * $Id: 519893a6517940c2efd8ea989a54657275dcf902 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/HttpTask.php';

/**
 * A HTTP download task.
 *
 * Downloads a file via HTTP GET method and saves it to a specified directory
 *
 * @package phing.tasks.ext
 * @author  Ole Markus With <o.with@sportradar.com>
 * @version $Id: 519893a6517940c2efd8ea989a54657275dcf902 $
 */
class HttpGetTask extends HttpTask
{
    /**
     * Holds the filename to store the output in
     *
     * @var string
     */
    protected $filename = null;

    /**
     * Holds the save location
     *
     * @var string
     */
    protected $dir = null;

    /**
     * Holds value for "ssl_verify_peer" option
     *
     * @var boolean
     */
    protected $sslVerifyPeer = true;

    /**
     * Holds value for "follow_redirects" option
     *
     * @var null|bool
     */
    protected $followRedirects = null;

    /**
     * Holds the proxy
     *
     * @var string
     */
    protected $proxy = null;

    /**
     * @return HTTP_Request2
     * @throws BuildException
     * @throws HTTP_Request2_LogicException
     */
    protected function createRequest()
    {
        if (!isset($this->dir)) {
            throw new BuildException("Required attribute 'dir' is missing");
        }

        $config = array(
            'ssl_verify_peer' => $this->sslVerifyPeer
        );
        if (isset($this->proxy)) {
            $config['proxy'] = $this->proxy;
        }
        if (null !== $this->followRedirects) {
            $config['follow_redirects'] = $this->followRedirects;
        }

        $request = parent::createRequest();
        $request->setConfig($config);

        $this->log("Fetching " . $this->url);

        return $request;
    }

    /**
     * Saves the response body to a specified directory
     *
     * @param  HTTP_Request2_Response $response
     * @return void
     * @throws BuildException
     */
    protected function processResponse(HTTP_Request2_Response $response)
    {
        if ($response->getStatus() != 200) {
            throw new BuildException(
                "Request unsuccessful. Response from server: " . $response->getStatus()
                . " " . $response->getReasonPhrase()
            );
        }

        $content = $response->getBody();
        $disposition = $response->getHeader('content-disposition');

        if ($this->filename) {
            $filename = $this->filename;

        } elseif ($disposition && 0 == strpos($disposition, 'attachment')
            && preg_match('/filename="([^"]+)"/', $disposition, $m)
        ) {
            $filename = basename($m[1]);

        } else {
            $filename = basename(parse_url($this->url, PHP_URL_PATH));
        }

        if (!is_writable($this->dir)) {
            throw new BuildException("Cannot write to directory: " . $this->dir);
        }

        $filename = $this->dir . "/" . $filename;
        file_put_contents($filename, $content);

        $this->log("Contents from " . $this->url . " saved to $filename");
    }

    /**
     * Sets the filename to store the output in
     *
     * @param string $filename
     */
    public function setFilename($filename)
    {
        $this->filename = $filename;
    }

    /**
     * Sets the save location
     *
     * @param string $dir
     */
    public function setDir($dir)
    {
        $this->dir = $dir;
    }

    /**
     * Sets the ssl_verify_peer option
     *
     * @param bool $value
     */
    public function setSslVerifyPeer($value)
    {
        $this->sslVerifyPeer = $value;
    }

    /**
     * Sets the follow_redirects option
     *
     * @param bool $value
     */
    public function setFollowRedirects($value)
    {
        $this->followRedirects = $value;
    }

    /**
     * Sets the proxy
     *
     * @param string $proxy
     */
    public function setProxy($proxy)
    {
        $this->proxy = $proxy;
    }
}
<?php
/**
 * $Id: ab6a2d3903492a67c2a16f15ae667d8866af6c1e $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/HttpTask.php';

/**
 * A HTTP request task.
 * Making an HTTP request and try to match the response against an provided
 * regular expression.
 *
 * @package phing.tasks.ext
 * @author  Benjamin Schultz <bschultz@proqrent.de>
 * @version $Id: ab6a2d3903492a67c2a16f15ae667d8866af6c1e $
 * @since   2.4.1
 */
class HttpRequestTask extends HttpTask
{
    /**
     * Holds the regular expression that should match the response
     *
     * @var string
     */
    protected $responseRegex = '';

    /**
     * Whether to enable detailed logging
     *
     * @var boolean
     */
    protected $verbose = false;

    /**
     * Holds the events that will be logged
     *
     * @var array<string>
     */
    protected $observerEvents = array(
        'connect',
        'sentHeaders',
        'sentBodyPart',
        'receivedHeaders',
        'receivedBody',
        'disconnect',
    );

    /**
     * Holds the request method
     *
     * @var string
     */
    protected $method = null;

    /**
     * Holds additional post parameters for the request
     *
     * @var Parameter[]
     */
    protected $postParameters = array();

    /**
     * Sets the response regex
     *
     * @param string $regex
     */
    public function setResponseRegex($regex)
    {
        $this->responseRegex = $regex;
    }

    /**
     * Sets whether to enable detailed logging
     *
     * @param boolean $verbose
     */
    public function setVerbose($verbose)
    {
        $this->verbose = StringHelper::booleanValue($verbose);
    }

    /**
     * Sets a list of observer events that will be logged if verbose output is enabled.
     *
     * @param string $observerEvents List of observer events
     */
    public function setObserverEvents($observerEvents)
    {
        $this->observerEvents = array();

        $token = ' ,;';
        $ext = strtok($observerEvents, $token);

        while ($ext !== false) {
            $this->observerEvents[] = $ext;
            $ext = strtok($token);
        }
    }

    /**
     * The setter for the method
     * @param $method
     */
    public function setMethod($method)
    {
        $this->method = $method;
    }

    /**
     * Creates post body parameters for this request
     *
     * @return Parameter The created post parameter
     */
    public function createPostParameter()
    {
        $num = array_push($this->postParameters, new Parameter());

        return $this->postParameters[$num - 1];
    }

    /**
     * Load the necessary environment for running this task.
     *
     * @throws BuildException
     */
    public function init()
    {
        parent::init();

        $this->authScheme = HTTP_Request2::AUTH_BASIC;

        // Other dependencies that should only be loaded when class is actually used
        require_once 'HTTP/Request2/Observer/Log.php';
    }

    /**
     * Creates and configures an instance of HTTP_Request2
     *
     * @return HTTP_Request2
     */
    protected function createRequest()
    {
        $request = parent::createRequest();

        if ($this->method == HTTP_Request2::METHOD_POST) {
            $request->setMethod(HTTP_Request2::METHOD_POST);

            foreach ($this->postParameters as $postParameter) {
                $request->addPostParameter($postParameter->getName(), $postParameter->getValue());
            }
        }

        if ($this->verbose) {
            $observer = new HTTP_Request2_Observer_Log();

            // set the events we want to log
            $observer->events = $this->observerEvents;

            $request->attach($observer);
        }

        return $request;
    }

    /**
     * Checks whether response body matches the given regexp
     *
     * @param  HTTP_Request2_Response $response
     * @return void
     * @throws BuildException
     */
    protected function processResponse(HTTP_Request2_Response $response)
    {
        if ($this->responseRegex !== '') {
            $matches = array();
            preg_match($this->responseRegex, $response->getBody(), $matches);

            if (count($matches) === 0) {
                throw new BuildException('The received response body did not match the given regular expression');
            } else {
                $this->log('The response body matched the provided regex.');
            }
        }
    }
}
<?php
/*
 * $Id: 4b9d4bc0ada3044961fd261245d566a72b3789a9 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Base class for HTTP_Request2-backed tasks
 *
 * Handles nested <config /> and <header /> tags, contains a method for
 * HTTP_Request2 instance creation
 *
 * @package phing.tasks.ext
 * @author  Alexey Borzov <avb@php.net>
 * @version $Id: 4b9d4bc0ada3044961fd261245d566a72b3789a9 $
 */
abstract class HttpTask extends Task
{
    /**
     * Holds the request URL
     *
     * @var string
     */
    protected $url = null;

    /**
     * Prototype HTTP_Request2 object, cloned in createRequest()
     *
     * @var HTTP_Request2
     */
    protected $requestPrototype = null;

    /**
     * Holds additional header data
     *
     * @var Parameter[]
     */
    protected $headers = array();

    /**
     * Holds additional config data for HTTP_Request2
     *
     * @var Parameter[]
     */
    protected $configData = array();

    /**
     * Holds the authentication user name
     *
     * @var string
     */
    protected $authUser = null;

    /**
     * Holds the authentication password
     *
     * @var string
     */
    protected $authPassword = '';

    /**
     * Holds the authentication scheme
     *
     * @var string
     */
    protected $authScheme;

    /**
     * Load the necessary environment for running this task.
     *
     * @throws BuildException
     */
    public function init()
    {
        @include_once 'HTTP/Request2.php';

        if (!class_exists('HTTP_Request2')) {
            throw new BuildException(
                get_class($this) . ' depends on HTTP_Request2 being installed '
                . 'and on include_path.',
                $this->getLocation()
            );
        }
    }

    /**
     * Sets the request URL
     *
     * @param string $url
     */
    public function setUrl($url)
    {
        $this->url = $url;
    }

    /**
     * Sets the prototype object that will be cloned in createRequest()
     *
     * Used in tests to inject an instance of HTTP_Request2 containing a custom adapter
     *
     * @param HTTP_Request2 $request
     */
    public function setRequestPrototype(HTTP_Request2 $request)
    {
        $this->requestPrototype = $request;
    }

    /**
     * Creates and configures an instance of HTTP_Request2
     *
     * @return HTTP_Request2
     */
    protected function createRequest()
    {
        if (!$this->requestPrototype) {
            $request = new HTTP_Request2($this->url);

        } else {
            $request = clone $this->requestPrototype;
            $request->setUrl($this->url);
        }

        foreach (array_keys($this->getProject()->getProperties()) as $propName) {
            if (0 === strpos($propName, 'phing.http.')) {
                $request->setConfig(substr($propName, 11), $this->getProject()->getProperty($propName));
            }
        }

        // set the authentication data
        if (!empty($this->authUser)) {
            $request->setAuth(
                $this->authUser,
                $this->authPassword,
                $this->authScheme
            );
        }

        foreach ($this->configData as $config) {
            $request->setConfig($config->getName(), $config->getValue());
        }

        foreach ($this->headers as $header) {
            $request->setHeader($header->getName(), $header->getValue());
        }

        return $request;
    }

    /**
     * Processes the server's response
     *
     * @param  HTTP_Request2_Response $response
     * @return void
     * @throws BuildException
     */
    abstract protected function processResponse(HTTP_Request2_Response $response);

    /**
     * Makes a HTTP request and processes its response
     *
     * @throws BuildException
     */
    public function main()
    {
        if (!isset($this->url)) {
            throw new BuildException("Required attribute 'url' is missing");
        }

        try {
            $this->processResponse($this->createRequest()->send());
        } catch (HTTP_Request2_MessageException $e) {
            throw new BuildException($e);
        }
    }

    /**
     * Creates an additional header for this task
     *
     * @return Parameter The created header
     */
    public function createHeader()
    {
        $num = array_push($this->headers, new Parameter());

        return $this->headers[$num - 1];
    }

    /**
     * Creates a config parameter for this task
     *
     * @return Parameter The created config parameter
     */
    public function createConfig()
    {
        $num = array_push($this->configData, new Parameter());

        return $this->configData[$num - 1];
    }

    /**
     * Sets the authentication user name
     *
     * @param string $user
     */
    public function setAuthUser($user)
    {
        $this->authUser = $user;
    }

    /**
     * Sets the authentication password
     *
     * @param string $password
     */
    public function setAuthPassword($password)
    {
        $this->authPassword = $password;
    }

    /**
     * Sets the authentication scheme
     *
     * @param string $scheme
     */
    public function setAuthScheme($scheme)
    {
        $this->authScheme = $scheme;
    }
}
<?php
/**
 * INI file modification task for Phing, the PHP build tool.
 *
 * Based on http://ant-contrib.sourceforge.net/tasks/tasks/inifile.html
 *
 * PHP version 5
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL v3 or later http://www.gnu.org/licenses/lgpl.html
 * @link     http://www.phing.info/
 */

/**
 * Class for reading/writing ini config file
 *
 * This preserves comments etc, unlike parse_ini_file and is based heavily on
 * a solution provided at:
 * stackoverflow.com/questions/9594238/good-php-classes-that-manipulate-ini-files
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL v3 or later http://www.gnu.org/licenses/lgpl.html
 * @link     http://www.phing.info/
 */
class IniFileConfig
{
    /**
     * Lines of ini file
     *
     * @var array
     */
    protected $lines = array();

    /**
     * Read ini file
     *
     * @param string $file filename
     *
     * @return void
     */
    public function read($file)
    {
        $this->lines = array();

        $section = '';

        foreach (file($file) as $line) {
            if (preg_match('/^\s*(;.*)?$/', $line)) {
                // comment or whitespace
                $this->lines[] = array(
                    'type' => 'comment',
                    'data' => $line,
                    'section' => $section
                );
            } elseif (preg_match('/^\s?\[(.*)\]/', $line, $match)) {
                // section
                $section = $match[1];
                $this->lines[] = array(
                    'type' => 'section',
                    'data' => $line,
                    'section' => $section
                );
            } elseif (preg_match('/^\s*(.*?)\s*=\s*(.*?)\s*$/', $line, $match)) {
                // entry
                $this->lines[] = array(
                    'type' => 'entry',
                    'data' => $line,
                    'section' => $section,
                    'key' => $match[1],
                    'value' => $match[2]
                );
            }
        }
    }

    /**
     * Get value of given key in specified section
     *
     * @param string $section Section
     * @param string $key     Key
     *
     * @return void
     */
    public function get($section, $key)
    {
        foreach ($this->lines as $line) {
            if ($line['type'] != 'entry') {
                continue;
            }
            if ($line['section'] != $section) {
                continue;
            }
            if ($line['key'] != $key) {
                continue;
            }
            return $line['value'];
        }

        throw new RuntimeException('Missing Section or Key');
    }

    /**
     * Set key to value in specified section
     *
     * @param string $section Section
     * @param string $key     Key
     * @param string $value   Value
     *
     * @return void
     */
    public function set($section, $key, $value)
    {
        foreach ($this->lines as &$line) {
            if ($line['type'] != 'entry') {
                continue;
            }
            if ($line['section'] != $section) {
                continue;
            }
            if ($line['key'] != $key) {
                continue;
            }
            $line['value'] = $value;
            $line['data'] = $key . " = " . $value . PHP_EOL;
            return;
        }

        throw new RuntimeException('Missing Section or Key');
    }

    /**
     * Remove key/section from file.
     *
     * If key is not specified, then the entire section will be removed.
     *
     * @param string $section Section to manipulate/remove
     * @param string $key     Name of key to remove, might be null/empty
     *
     * @return void
     */
    public function remove($section, $key)
    {
        if ($section == '') {
            throw new RuntimeException("Section not set.");
        }
        if (is_null($key) || ($key == '')) {
            // remove entire section
            foreach ($this->lines as $linenum => $line) {
                if ($line['section'] == $section) {
                    unset($this->lines[$linenum]);
                }
            }
        } else {
            foreach ($this->lines as $linenum => $line) {
                if (($line['section'] == $section)
                    && (isset($line['key']))
                    && ($line['key'] == $key)
                ) {
                    unset($this->lines[$linenum]);
                }
            }
        }
    }

    /**
     * Write contents out to file
     *
     * @param string $file filename
     *
     * @return void
     */
    public function write($file)
    {
        if (file_exists($file) && !is_writable($file)) {
            throw new RuntimeException("$file is not writable");
        }
        $fp = fopen($file, 'w');
        foreach ($this->lines as $line) {
            fwrite($fp, $line['data']);
        }
        fclose($fp);
    }
}
<?php
/**
 * INI file modification task for Phing, the PHP build tool.
 *
 * Based on http://ant-contrib.sourceforge.net/tasks/tasks/inifile.html
 *
 * PHP version 5
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL v3 or later http://www.gnu.org/licenses/lgpl.html
 * @link     http://www.phing.info/
 */

/**
 * Class for collecting details for removing keys or sections from an ini file
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL v3 or later http://www.gnu.org/licenses/lgpl.html
 * @link     http://www.phing.info/
 */
class IniFileRemove
{
    /**
     * Property
     *
     * @var string
     */
    protected $property = null;

    /**
     * Section
     *
     * @var string
     */
    protected $section = null;

    /**
     * Set Section name
     *
     * @param string $section Name of section in ini file
     *
     * @return void
     */
    public function setSection($section)
    {
        $this->section = $section;
    }

    /**
     * Set Property/Key name
     *
     * @param string $property ini key name
     *
     * @return void
     */
    public function setProperty($property)
    {
        $this->property = $property;
    }

    /**
     * Get Property
     *
     * @return string
     */
    public function getProperty()
    {
        return $this->property;
    }

    /**
     * Get Section
     *
     * @return string
     */
    public function getSection()
    {
        return $this->section;
    }
}
<?php
/**
 * Class for collecting details for setting values in ini file
 *
 * Based on http://ant-contrib.sourceforge.net/tasks/tasks/inifile.html
 *
 * PHP version 5
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL v3 or later http://www.gnu.org/licenses/lgpl.html
 * @link     http://www.phing.info/
 */

/**
 * InifileSet
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <ken@linux.ie>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     InifileSet.php
 */
class IniFileSet
{
    /**
     * Property
     *
     * @var string
     */
    protected $property = null;

    /**
     * Section
     *
     * @var string
     */
    protected $section = null;

    /**
     * Value
     *
     * @var mixed
     */
    protected $value = null;

    /**
     * Operation
     *
     * @var mixed
     */
    protected $operation = null;

    /**
     * Set Operation
     *
     * @param string $operation +/-
     *
     * @return void
     */
    public function setOperation($operation)
    {
        $this->operation = $operation;
    }

    /**
     * Get Operation
     *
     * @return void
     */
    public function getOperation()
    {
        return $this->operation;
    }

    /**
     * Set Section name
     *
     * @param string $section Name of section in ini file
     *
     * @return void
     */
    public function setSection($section)
    {
        $this->section = trim($section);
    }

    /**
     * Set Property
     *
     * @param string $property property/key name
     *
     * @return void
     */
    public function setProperty($property)
    {
        $this->property = $property;
    }

    /**
     * Set Value
     *
     * @param string $value Value to set for key in ini file
     *
     * @return void
     */
    public function setValue($value)
    {
        $this->value = $value;
    }

    /**
     * Get Property
     *
     * @return string
     */
    public function getProperty()
    {
        return $this->property;
    }

    /**
     * Get Value
     *
     * @return string
     */
    public function getValue()
    {
        return $this->value;
    }
    /**
     * Get Section
     *
     * @return string
     */
    public function getSection()
    {
        return $this->section;
    }
}
<?php
/**
 * INI file modification task for Phing, the PHP build tool.
 *
 * Based on http://ant-contrib.sourceforge.net/tasks/tasks/inifile.html
 *
 * PHP version 5
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL v3 or later http://www.gnu.org/licenses/lgpl.html
 * @link     http://www.phing.info/
 */

require_once 'IniFileSet.php';
require_once 'IniFileRemove.php';
require_once 'IniFileConfig.php';

/**
 * InifileTask
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <ken@linux.ie>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     InifileTask.php
 */
class InifileTask extends Task
{
    /**
     * Source file
     *
     * @var string|null
     */
    protected $source = null;
    /**
     * Dest file
     *
     * @var string|null
     */
    protected $dest = null;

    /**
     * Whether to halt phing on error.
     *
     * @var bool
     */
    protected $haltonerror = false;
    /**
     * Sets
     *
     * @var array
     */
    protected $sets = array();
    /**
     * Removals
     *
     * @var array
     */
    protected $removals = array();

    /**
     * IniFileConfig instance
     *
     * @var IniFileConfig
     */
    protected $ini = null;

    /**
     * Taskname for logger
     * @var string
     */
    protected $taskName = 'IniFile';

    /**
     * Check file to be read from
     *
     * @param string $readFile Filename
     *
     * @return void
     */
    public function checkReadFile($readFile)
    {
        if (is_null($readFile)) {
            return false;
        }
        if (!file_exists($readFile)) {
            $msg = "$readFile does not exist.";
            if ($this->haltonerror) {
                throw new BuildException($msg);
            }
            $this->log($msg, Project::MSG_ERR);
            return false;
        }
        if (!is_readable($readFile)) {
            $msg = "$readFile is not readable.";
            if ($this->haltonerror) {
                throw new BuildException($msg);
            }
            $this->log($msg, Project::MSG_ERR);
            return false;
        }
        $this->ini->read($readFile);
        $this->log("Read from $readFile");
        return true;
    }

    /**
     * Check file to write to
     *
     * @param string $writeFile Filename
     *
     * @return void
     */
    public function checkWriteFile($writeFile)
    {
        if (file_exists($writeFile) && !is_writable($writeFile)) {
            $msg = "$writeFile is not writable";
            if ($this->haltonerror) {
                throw new BuildException($msg);
            }
            $this->log($msg, Project::MSG_ERR);
            return false;
        }
        return true;
    }

    /**
     * The main entry point method.
     *
     * @return void
     */
    public function main()
    {
        $this->ini = new IniFileConfig();
        $readFile = null;
        $writeFile = null;

        if (!is_null($this->source) && is_null($this->dest)) {
            $readFile = $this->source;
        } elseif (!is_null($this->dest) && is_null($this->source)) {
            $readFile = $this->dest;
        } else {
            $readFile = $this->source;
        }

        if (!is_null($this->dest)) {
            $writeFile = $this->dest;
        } elseif (!is_null($this->source)) {
            $writeFile = $this->source;
        } else {
            $writeFile = $this->dest;
        }

        if ($readFile === null && $writeFile === null) {
            $msg = "Neither source nor dest is set";
            if ($this->haltonerror) {
                throw new BuildException($msg);
            }
            $this->log($msg, Project::MSG_ERR);
            return;
        }

        if (!$this->checkReadFile($readFile)) {
            return;
        }

        if (!$this->checkWriteFile($writeFile)) {
            return;
        }

        $this->enumerateSets();
        $this->enumerateRemoves();
        try {
            $this->ini->write($writeFile);
            $this->log("Wrote to $writeFile");
        } catch (Exception $ex) {
            $msg = $ex->getMessage();
            if ($this->haltonerror) {
                throw new BuildException($msg);
            }
            $this->log($msg, Project::MSG_ERR);
        }
    }

    /**
     * Work through all Set commands.
     *
     * @return void
     */
    public function enumerateSets()
    {
        foreach ($this->sets as $set) {
            $value = $set->getValue();
            $key = $set->getProperty();
            $section = $set->getSection();
            $operation = $set->getOperation();
            if ($value !== null) {
                try {
                    $this->ini->set($section, $key, $value);
                    $this->log(
                        "[$section] $key set to $value",
                        Project::MSG_DEBUG
                    );
                } catch (Exception $ex) {
                    $this->log(
                        "Error setting value for section '" . $section .
                        "', key '" . $key ."'"
                    );
                    $this->log($ex->getMessage(), Project::MSG_DEBUG);
                }
            } elseif ($operation !== null) {
                $v = $this->ini->get($section, $key);
                // value might be wrapped in quotes with a semicolon at the end
                if (!is_numeric($v)) {
                    if (preg_match('/^"(\d*)";?$/', $v, $match)) {
                        $v = $match[1];
                    } elseif (preg_match("/^'(\d*)';?$/", $v, $match)) {
                        $v = $match[1];
                    } else {
                        $this->log(
                            "Value $v is not numeric. Skipping $operation operation."
                        );
                        continue;
                    }
                }
                if ($operation == '+') {
                    ++$v;
                } elseif ($operation == '-') {
                    --$v;
                } else {
                    if (($operation != '-') && ($operation != '+')) {
                        $msg = "Unrecognised operation $operation";
                        if ($this->haltonerror) {
                            throw new BuildException($msg);
                        }
                        $this->log($msg, Project::MSG_ERR);
                    }
                }
                try {
                    $this->ini->set($section, $key, $v);
                    $this->log(
                        "[$section] $key set to $v",
                        Project::MSG_DEBUG
                    );
                } catch (Exception $ex) {
                    $this->log(
                        "Error setting value for section '" . $section .
                        "', key '" . $key ."'"
                    );
                    $this->log($ex->getMessage(), Project::MSG_DEBUG);
                }
            } else {
                $this->log(
                    "Set: value and operation are both not set",
                    Project::MSG_ERR
                );
            }
        }
    }

    /**
     * Work through all Remove commands.
     *
     * @return void
     */
    public function enumerateRemoves()
    {
        foreach ($this->removals as $remove) {
            $key = $remove->getProperty();
            $section = $remove->getSection();
            if ($section == '') {
                $this->log(
                    "Remove: section must be set",
                    Project::MSG_ERR
                );
                continue;
            }
            $this->ini->remove($section, $key);
            if (($section != '') && ($key != '')) {
                $this->log(
                    "$key in section [$section] has been removed.",
                    Project::MSG_DEBUG
                );
            } elseif (($section != '') && ($key == '')) {
                $this->log("[$section] has been removed.", Project::MSG_DEBUG);
            }
        }
    }

    /**
     * Set Source property
     *
     * @param string $source Name of originating ini file to parse
     *
     * @return void
     */
    public function setSource($source)
    {
        $this->source = $source;
    }

    /**
     * Set Dest property
     *
     * @param string $dest Destination filename to write ini contents to.
     *
     * @return void
     */
    public function setDest($dest)
    {
        $this->dest = $dest;
    }

    /**
     * Set haltonerror attribute.
     *
     * @param string $halt 'yes', or '1' to halt.
     *
     * @return void
     */
    public function setHaltonerror($halt)
    {
        $doHalt = false;
        if (strtolower($halt) == 'yes' || $halt == 1) {
            $doHalt = true;
        }
        $this->haltonerror = $doHalt;
    }

    /**
     * Create a Set method
     *
     * @return IniFileSet
     */
    public function createSet()
    {
        $set = new IniFileSet();
        $this->sets[] = $set;
        return $set;
    }

    /**
     * Create a Remove method
     *
     * @return IniFileRemove
     */
    public function createRemove()
    {
        $remove = new IniFileRemove();
        $this->removals[] = $remove;
        return $remove;
    }
}
<?php
/**
 * $Id: a07beee075383ef21856e1931b9867085b033b67 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Wrapper for comments for ionCube tasks
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: a07beee075383ef21856e1931b9867085b033b67 $
 * @package phing.tasks.ext.ioncube
 * @since 2.2.0
 */
class IoncubeComment
{
    private $value = "";

    /**
     * @return string
     */
    public function getValue()
    {
        return $this->value;
    }

    /**
     * @param $txt
     */
    public function addText($txt)
    {
        $this->value = trim($txt);
    }
}
<?php
/**
 * $Id: 03f95f42b0965ac16a78438fbc7e77c9eaf89e9c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/ioncube/IoncubeComment.php';

/**
 * Invokes the ionCube Encoder (PHP4 or PHP5)
 *
 * @author Michiel Rook <mrook@php.net>
 * @author Andrew Eddie <andrew.eddie@jamboworks.com>
 * @author Domenico Sgarbossa <sbraaaa@yahoo.it>
 * @version $Id: 03f95f42b0965ac16a78438fbc7e77c9eaf89e9c $
 * @package phing.tasks.ext.ioncube
 * @since 2.2.0
 */
class IoncubeEncoderTask extends Task
{
    private $ionSwitches = array();

    private $ionOptions = array();

    private $ionOptionsXS = array();

    private $comments = array();

    private $encoderName = 'ioncube_encoder';

    private $fromDir = '';

    private $ioncubePath = '/usr/local/ioncube';

    private $phpVersion = '5';

    private $targetOption = '';

    private $toDir = '';

    private $showCommandLine = false;

    /**
     * Sets whether to show command line before it is executed
     * @param $value
     */
    public function setShowCommandLine($value)
    {
        $this->showCommandLine = $value;
    }

    /**
     * Adds a comment to be used in encoded files
     * @param IoncubeComment $comment
     */
    public function addComment(IoncubeComment $comment)
    {
        $this->comments[] = $comment;
    }

    /**
     * Sets the allowed server
     * @param $value
     */
    public function setAllowedServer($value)
    {
        $this->ionOptionsXS['allowed-server'] = $value;
    }

    /**
     * Returns the allowed server setting
     */
    public function getAllowedServer()
    {
        return $this->ionOptionsXS['allowed-server'];
    }

    /**
     * Sets the binary option
     * @param $value
     */
    public function setBinary($value)
    {
        $this->ionSwitches['binary'] = $value;
    }

    /**
     * Returns the binary option
     */
    public function getBinary()
    {
        return $this->ionSwitches['binary'];
    }

    /**
     * Sets files or folders to copy (separated by space)
     * @param $value
     */
    public function setCopy($value)
    {
        $this->ionOptionsXS['copy'] = $value;
    }

    /**
     * Returns the copy setting
     */
    public function getCopy()
    {
        return $this->ionOptionsXS['copy'];
    }

    /**
     * Sets additional file patterns, files or directories to encode,
     * or to reverse the effect of copy (separated by space)
     * @param $value
     */
    public function setEncode($value)
    {
        $this->ionOptionsXS['encode'] = $value;
    }

    /**
     * Returns the encode setting
     */
    public function getEncode()
    {
        return $this->ionOptionsXS['encode'];
    }

    /**
     * Sets regexps of additional files to encrypt (separated by space)
     * @param $value
     */
    public function setEncrypt($value)
    {
        $this->ionOptionsXS['encrypt'] = $value;
    }

    /**
     * Returns regexps of additional files to encrypt (separated by space)
     */
    public function getEncrypt()
    {
        return $this->ionOptionsXS['encrypt'];
    }

    /**
     * Sets a period after which the files expire
     * @param $value
     */
    public function setExpirein($value)
    {
        $this->ionOptions['expire-in'] = $value;
    }

    /**
     * Returns the expireIn setting
     */
    public function getExpirein()
    {
        return $this->ionOptions['expire-in'];
    }

    /**
     * Sets a YYYY-MM-DD date to expire the files
     * @param $value
     */
    public function setExpireon($value)
    {
        $this->ionOptions['expire-on'] = $value;
    }

    /**
     * Returns the expireOn setting
     */
    public function getExpireon()
    {
        return $this->ionOptions['expire-on'];
    }

    /**
     * Sets the source directory
     * @param $value
     */
    public function setFromDir($value)
    {
        $this->fromDir = $value;
    }

    /**
     * Returns the source directory
     */
    public function getFromDir()
    {
        return $this->fromDir;
    }

    /**
     * Set files and directories to ignore entirely and exclude from the target directory
     * (separated by space).
     * @param $value
     */
    public function setIgnore($value)
    {
        $this->ionOptionsXS['ignore'] = $value;
    }

    /**
     * Returns the ignore setting
     */
    public function getIgnore()
    {
        return $this->ionOptionsXS['ignore'];
    }

    /**
     * Sets the path to the ionCube encoder
     * @param $value
     */
    public function setIoncubePath($value)
    {
        $this->ioncubePath = $value;
    }

    /**
     * Returns the path to the ionCube encoder
     */
    public function getIoncubePath()
    {
        return $this->ioncubePath;
    }

    /**
     * Set files and directories not to be ignored (separated by space).
     * @param $value
     */
    public function setKeep($value)
    {
        $this->ionOptionsXS['keep'] = $value;
    }

    /**
     * Returns the ignore setting
     */
    public function getKeep()
    {
        return $this->ionOptionsXS['keep'];
    }

    /**
     * Sets the path to the license file to use
     * @param $value
     */
    public function setLicensePath($value)
    {
        $this->ionOptions['with-license'] = $value;
    }

    /**
     * Returns the path to the license file to use
     */
    public function getLicensePath()
    {
        return $this->ionOptions['with-license'];
    }

    /**
     * Sets the no-doc-comments option
     * @param $value
     */
    public function setNoDocComments($value)
    {
        $this->ionSwitches['no-doc-comment'] = $value;
    }

    /**
     * Returns the no-doc-comments option
     */
    public function getNoDocComments()
    {
        return $this->ionSwitches['no-doc-comment'];
    }

    /**
     * Sets the obfuscate option
     * @param $value
     */
    public function setObfuscate($value)
    {
        $this->ionOptionsXS['obfuscate'] = $value;
    }

    /**
     * Returns the optimize option
     */
    public function getObfuscate()
    {
        return $this->ionOptionsXS['obfuscate'];
    }

    /**
     * Sets the obfuscation key (required if using the obfuscate option)
     * @param $value
     */
    public function setObfuscationKey($value)
    {
        $this->ionOptions['obfuscation-key'] = $value;
    }

    /**
     * Returns the optimize option
     */
    public function getObfuscationKey()
    {
        return $this->ionOptions['obfuscation-key'];
    }

    /**
     * Sets the optimize option
     * @param $value
     */
    public function setOptimize($value)
    {
        $this->ionOptions['optimize'] = $value;
    }

    /**
     * Returns the optimize option
     */
    public function getOptimize()
    {
        return $this->ionOptions['optimize'];
    }

    /**
     * Sets the passphrase to use when encoding files
     * @param $value
     */
    public function setPassPhrase($value)
    {
        $this->ionOptions['passphrase'] = $value;
    }

    /**
     * Returns the passphrase to use when encoding files
     */
    public function getPassPhrase()
    {
        return $this->ionOptions['passphrase'];
    }

    /**
     * Sets the version of PHP to use (defaults to 5)
     * @param $value
     */
    public function setPhpVersion($value)
    {
        $this->phpVersion = $value;
    }

    /**
     * Returns the version of PHP to use (defaults to 5)
     */
    public function getPhpVersion()
    {
        return $this->phpVersion;
    }

    /**
     * Sets the target directory
     * @param $value
     */
    public function setToDir($value)
    {
        $this->toDir = $value;
    }

    /**
     * Returns the target directory
     */
    public function getToDir()
    {
        return $this->toDir;
    }

    /**
     * Sets the without-runtime-loader-support option
     * @param $value
     */
    public function setWithoutRuntimeLoaderSupport($value)
    {
        $this->ionSwitches['without-runtime-loader-support'] = $value;
    }

    /**
     * Returns the without-runtime-loader-support option
     */
    public function getWithoutRuntimeLoaderSupport()
    {
        return $this->ionSwitches['without-runtime-loader-support'];
    }

    /**
     * Sets the no-short-open-tags option
     * @param $value
     */
    public function setNoShortOpenTags($value)
    {
        $this->ionSwitches['no-short-open-tags'] = $value;
    }

    /**
     * Returns the no-short-open-tags option
     */
    public function getNoShortOpenTags()
    {
        return $this->ionSwitches['no-short-open-tags'];
    }

    /**
     * Sets the ignore-deprecated-warnings option
     * @param $value
     */
    public function setIgnoreDeprecatedWarnings($value)
    {
        $this->ionSwitches['ignore-deprecated-warnings'] = $value;
    }

    /**
     * Returns the ignore-deprecated-warnings option
     */
    public function getIgnoreDeprecatedWarnings()
    {
        return $this->ionSwitches['ignore-deprecated-warnings'];
    }

    /**
     * Sets the ignore-strict-warnings option
     * @param $value
     */
    public function setIgnoreStrictWarnings($value)
    {
        $this->ionSwitches['ignore-strict-warnings'] = $value;
    }

    /**
     * Returns the ignore-strict-warnings option
     */
    public function getIgnoreStrictWarnings()
    {
        return $this->ionSwitches['ignore-strict-warnings'];
    }

    /**
     * Sets the allow-encoding-into-source option
     * @param $value
     */
    public function setAllowEncodingIntoSource($value)
    {
        $this->ionSwitches['allow-encoding-into-source'] = $value;
    }

    /**
     * Returns the allow-encoding-into-source option
     */
    public function getAllowEncodingIntoSource()
    {
        return $this->ionSwitches['allow-encoding-into-source'];
    }

    /**
     * Sets the message-if-no-loader option
     * @param $value
     */
    public function setMessageIfNoLoader($value)
    {
        $this->ionOptions['message-if-no-loader'] = $value;
    }

    /**
     * Returns the message-if-no-loader option
     */
    public function getMessageIfNoLoader()
    {
        return $this->ionOptions['message-if-no-loader'];
    }

    /**
     * Sets the action-if-no-loader option
     * @param $value
     */
    public function setActionIfNoLoader($value)
    {
        $this->ionOptions['action-if-no-loader'] = $value;
    }

    /**
     * Returns the action-if-no-loader option
     */
    public function getActionIfNoLoader()
    {
        return $this->ionOptions['action-if-no-loader'];
    }

    /**
     * Sets the option to use when encoding target directory already exists (defaults to none)
     * @param $targetOption
     */
    public function setTargetOption($targetOption)
    {
        $this->targetOption = $targetOption;
    }

    /**
     * Returns the option to use when encoding target directory already exists (defaults to none)
     */
    public function getTargetOption()
    {
        return $this->targetOption;
    }

    /**
     * Sets the callback-file option
     * @param $value
     */
    public function setCallbackFile($value)
    {
        $this->ionOptions['callback-file'] = $value;
    }

    /**
     * Returns the callback-file option
     */
    public function getCallbackFile()
    {
        return $this->ionOptions['callback-file'];
    }

    /**
     * Sets the obfuscation-exclusions-file option
     * @param $value
     */
    public function setObfuscationExclusionFile($value)
    {
        $this->ionOptions['obfuscation-exclusion-file'] = $value;
    }

    /**
     * Returns the obfuscation-exclusions-file option
     */
    public function getObfuscationExclusionFile()
    {
        return $this->ionOptions['obfuscation-exclusion-file'];
    }

    /**
     * The main entry point
     *
     * @throws BuildException
     */
    public function main()
    {
        $arguments = $this->constructArguments();

        if (in_array($this->phpVersion, array(5, 53, 54, 55, 56))) {
            $encoderName = $this->encoderName . $this->phpVersion;
        } else {
            $encoderName = $this->encoderName;
        }
        $encoder = new PhingFile($this->ioncubePath, $encoderName);

        $this->log("Running ionCube Encoder...");

        if ($this->showCommandLine) {
            $this->log("Command line: " . $encoder->__toString() . ' ' . $arguments);
        }

        exec($encoder->__toString() . ' ' . $arguments . " 2>&1", $output, $return);

        if ($return != 0) {
            throw new BuildException("Could not execute ionCube Encoder: " . implode(' ', $output));
        }
    }

    /**
     * Constructs an argument string for the ionCube encoder
     */
    private function constructArguments()
    {
        $arguments = '';

        foreach ($this->ionSwitches as $name => $value) {
            if ($value) {
                $arguments .= "--$name ";
            }
        }

        foreach ($this->ionOptions as $name => $value) {
            /**
             * action-if-no-loader value is a php source snippet so it is
             * better to handle it this way to prevent quote problems!
             */
            if ($name == 'action-if-no-loader') {
                $arguments .= "--$name \"$value\" ";
            } else {
                $arguments .= "--$name '$value' ";
            }
        }

        foreach ($this->ionOptionsXS as $name => $value) {
            foreach (explode(' ', $value) as $arg) {
                $arguments .= "--$name '$arg' ";
            }
        }

        foreach ($this->comments as $comment) {
            $arguments .= "--add-comment '" . $comment->getValue() . "' ";
        }

        if (!empty($this->targetOption)) {
            switch ($this->targetOption) {
                case "replace":
                case "merge":
                case "update":
                case "rename":
                {
                    $arguments .= "--" . $this->targetOption . "-target ";
                }
                    break;

                default:
                    {
                    throw new BuildException("Unknown target option '" . $this->targetOption . "'");
                    }
                    break;
            }
        }

        if ($this->fromDir != '') {
            $arguments .= $this->fromDir . ' ';
        }

        if ($this->toDir != '') {
            $arguments .= "-o " . $this->toDir . ' ';
        }

        return $arguments;
    }
}
<?php
/**
 * $Id: 46bbb0c463665f3960cef69b836652c4f3f4c32c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/ioncube/IoncubeComment.php';

/**
 * Invokes the ionCube "make_license" program
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 46bbb0c463665f3960cef69b836652c4f3f4c32c $
 * @package phing.tasks.ext.ioncube
 * @since 2.2.0
 */
class IoncubeLicenseTask extends Task
{
    private $ioncubePath = "/usr/local/ioncube";

    private $licensePath = "";
    private $passPhrase = "";
    private $allowedServer = "";
    private $expireOn = "";
    private $expireIn = "";
    private $comments = array();

    /**
     * Sets the path to the ionCube encoder
     * @param $ioncubePath
     */
    public function setIoncubePath($ioncubePath)
    {
        $this->ioncubePath = $ioncubePath;
    }

    /**
     * Returns the path to the ionCube encoder
     */
    public function getIoncubePath()
    {
        return $this->ioncubePath;
    }

    /**
     * Sets the path to the license file to use
     * @param $licensePath
     */
    public function setLicensePath($licensePath)
    {
        $this->licensePath = $licensePath;
    }

    /**
     * Returns the path to the license file to use
     */
    public function getLicensePath()
    {
        return $this->licensePath;
    }

    /**
     * Sets the passphrase to use when encoding files
     * @param $passPhrase
     */
    public function setPassPhrase($passPhrase)
    {
        $this->passPhrase = $passPhrase;
    }

    /**
     * Returns the passphrase to use when encoding files
     */
    public function getPassPhrase()
    {
        return $this->passPhrase;
    }

    /**
     * Adds a comment to be used in encoded files
     * @param IoncubeComment $comment
     */
    public function addComment(IoncubeComment $comment)
    {
        $this->comments[] = $comment;
    }

    /**
     * Sets the --allowed-server option to use when generating the license
     * @param $allowedServer
     */
    public function setAllowedServer($allowedServer)
    {
        $this->allowedServer = $allowedServer;
    }

    /**
     * Returns the --allowed-server option
     */
    public function getAllowedServer()
    {
        return $this->allowedServer;
    }

    /**
     * Sets the --expire-on option to use when generating the license
     * @param $expireOn
     */
    public function setExpireOn($expireOn)
    {
        $this->expireOn = $expireOn;
    }

    /**
     * Returns the --expire-on option
     */
    public function getExpireOn()
    {
        return $this->expireOn;
    }

    /**
     * Sets the --expire-in option to use when generating the license
     * @param $expireIn
     */
    public function setExpireIn($expireIn)
    {
        $this->expireIn = $expireIn;
    }

    /**
     * Returns the --expire-in option
     */
    public function getExpireIn()
    {
        return $this->expireIn;
    }

    /**
     * The main entry point
     *
     * @throws BuildException
     */
    public function main()
    {
        $arguments = $this->constructArguments();

        $makelicense = new PhingFile($this->ioncubePath, 'make_license');

        $this->log("Running ionCube make_license...");

        exec($makelicense->__toString() . " " . $arguments . " 2>&1", $output, $return);

        if ($return != 0) {
            throw new BuildException("Could not execute ionCube make_license: " . implode(' ', $output));
        }
    }

    /**
     * Constructs an argument string for the ionCube make_license
     */
    private function constructArguments()
    {
        $arguments = "";

        if (!empty($this->passPhrase)) {
            $arguments .= "--passphrase '" . $this->passPhrase . "' ";
        }

        foreach ($this->comments as $comment) {
            $arguments .= "--header-line '" . $comment->getValue() . "' ";
        }

        if (!empty($this->licensePath)) {
            $arguments .= "--o '" . $this->licensePath . "' ";
        }

        if (!empty($this->allowedServer)) {
            $arguments .= "--allowed-server {" . $this->allowedServer . "} ";
        }

        if (!empty($this->expireOn)) {
            $arguments .= "--expire-on " . $this->expireOn . " ";
        }

        if (!empty($this->expireIn)) {
            $arguments .= "--expire-in " . $this->expireIn . " ";
        }

        return $arguments;
    }
}
<?php
/*
 *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
        * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
        * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://phing.info>.
*/

require_once 'phing/Task.php';

/**
 * JsHintTask
 *
 * Checks the JavaScript code using JSHint
 * See http://www.jshint.com/
 *
 * @author Martin Hujer <mhujer@gmail.com>
 * @package phing.tasks.ext
 * @version $Id: a34b2bbc6ba4e40dbb4a9fa57bde9ca8ce7ef3e5 $
 * @since 2.6.2
 */
class JsHintTask extends Task
{

    /**
     * The source file (from xml attribute)
     *
     * @var string
     */
    protected $file;

    /**
     * All fileset objects assigned to this task
     *
     * @var FileSet[]
     */
    protected $filesets = array();

    /**
     * Should the build fail on JSHint errors
     *
     * @var boolean
     */
    private $haltOnError = false;

    /**
     * Should the build fail on JSHint warnings
     *
     * @var boolean
     */
    private $haltOnWarning = false;

    /**
     * reporter
     *
     * @var string
     */
    private $reporter = 'checkstyle';

    /**
     * xmlAttributes
     *
     * @var array
     */
    private $xmlAttributes = array(
        'severity' => array(
            'error' => 'error',
            'warning' => 'warning',
            'info' => 'info'
        ),
        'fileError' => 'error',
        'line' => 'line',
        'column' => 'column',
        'message' => 'message',
    );

    /**
     * Path where the the report in Checkstyle format should be saved
     *
     * @var string
     */
    private $checkstyleReportPath;
    
    /** @var string $config */
    private $config;

    /**
     * File to be performed syntax check on
     *
     * @param PhingFile $file
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * @param $haltOnError
     */
    public function setHaltOnError($haltOnError)
    {
        $this->haltOnError = $haltOnError;
    }

    /**
     * @param $haltOnWarning
     */
    public function setHaltOnWarning($haltOnWarning)
    {
        $this->haltOnWarning = $haltOnWarning;
    }

    /**
     * @param $checkstyleReportPath
     */
    public function setCheckstyleReportPath($checkstyleReportPath)
    {
        $this->checkstyleReportPath = $checkstyleReportPath;
    }

    /**
     * @param string $reporter
     */
    public function setReporter($reporter)
    {
        $this->reporter = $reporter;

        if ($this->reporter === 'jslint') {
            $this->xmlAttributes = array(
                'severity' => array('error' => 'E', 'warning' => 'W', 'info' => 'I'),
                'fileError' => 'issue',
                'line' => 'line',
                'column' => 'char',
                'message' => 'reason',
            );
        }
    }

    /**
     * @param string $config
     */
    public function setConfig($config)
    {
        $this->config = $config;
    }
    
    public function main()
    {
        if (!isset($this->file) && count($this->filesets) === 0) {
            throw new BuildException("Missing either a nested fileset or attribute 'file' set");
        }

        if (!isset($this->file)) {
            $fileList = array();
            $project = $this->getProject();
            foreach ($this->filesets as $fs) {
                $ds = $fs->getDirectoryScanner($project);
                $files = $ds->getIncludedFiles();
                $dir = $fs->getDir($this->project)->getAbsolutePath();
                foreach ($files as $file) {
                    $fileList[] = $dir . DIRECTORY_SEPARATOR . $file;
                }
            }
        } else {
            $fileList = array($this->file);
        }

        $this->_checkJsHintIsInstalled();

        $fileList = array_map('escapeshellarg', $fileList);
        if ($this->config) {
            $command = sprintf(
                'jshint --config=%s --reporter=%s %s',
                $this->config,
                $this->reporter,
                implode(' ', $fileList)
            );
        } else {
            $command = sprintf(
                'jshint --reporter=%s %s',
                $this->reporter,
                implode(' ', $fileList)
            );
        }
        $output = array();
        exec($command, $output);
        $output = implode(PHP_EOL, $output);
        $xml = simplexml_load_string($output);

        $projectBasedir = $this->_getProjectBasedir();
        $errorsCount = 0;
        $warningsCount = 0;
        foreach ($xml->file as $file) {
            $fileAttributes = $file->attributes();
            $fileName = (string) $fileAttributes['name'];
            foreach ($file->error as $error) {
                $errAttr = (array) $error->attributes();
                $attrs = current($errAttr);

                if ($attrs['severity'] === $this->xmlAttributes['severity']['error']) {
                    $errorsCount++;
                } elseif ($attrs['severity'] === $this->xmlAttributes['severity']['warning']) {
                    $warningsCount++;
                } elseif ($attrs['severity'] !== $this->xmlAttributes['severity']['info']) {
                    throw new BuildException(sprintf('Unknown severity "%s"', $attrs['severity']));
                }
                $e = sprintf(
                    '%s: line %d, col %d, %s',
                    str_replace($projectBasedir, '', $fileName),
                    $attrs[$this->xmlAttributes['line']],
                    $attrs[$this->xmlAttributes['column']],
                    $attrs[$this->xmlAttributes['message']]
                );
                $this->log($e);
            }
        }

        $message = sprintf(
            'JSHint detected %d errors and %d warnings.',
            $errorsCount,
            $warningsCount
        );
        if ($this->haltOnError && $errorsCount) {
            throw new BuildException($message);
        } elseif ($this->haltOnWarning && $warningsCount) {
            throw new BuildException($message);
        } else {
            $this->log('');
            $this->log($message);
        }

        if ($this->checkstyleReportPath) {
            file_put_contents($this->checkstyleReportPath, $output);
            $this->log('');
            $this->log('Checkstyle report saved to ' . $this->checkstyleReportPath);
        }
    }

    /**
     * @return string Path to the project basedir
     * @throws \BuildException
     */
    private function _getProjectBasedir()
    {
        return $this->getProject()->getBasedir()->getAbsolutePath() . DIRECTORY_SEPARATOR;
    }

    /**
     * Checks, wheter the JSHint can be executed
     * @throws \BuildException
     */
    private function _checkJsHintIsInstalled()
    {
        exec('jshint -v', $output, $return);
        if ($return !== 0) {
            throw new BuildException('JSHint is not installed!');
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/util/DataStore.php';

/**
 * A Javascript lint task. Checks syntax of Javascript files.
 * Javascript lint (http://www.javascriptlint.com) must be in the system path.
 * This class is based on Knut Urdalen's PhpLintTask.
 *
 * @author Stefan Priebsch <stefan.priebsch@e-novative.de>
 *
 * @package phing.tasks.ext
 */
class JslLintTask extends Task
{
    /** @var PhingFile */
    protected $file; // the source file (from xml attribute)

    /** @var array */
    protected $filesets = array(); // all fileset objects assigned to this task

    /** @var bool $showWarnings */
    protected $showWarnings = true;

    /** @var bool */
    protected $haltOnFailure = false;

    /**
     * @var bool
     */
    protected $haltOnWarning = false;

    /**
     * @var bool
     */
    protected $hasErrors = false;

    /**
     * @var bool
     */
    protected $hasWarnings = false;

    /** @var array $badFiles */
    private $badFiles = array();

    /** @var DataStore */
    private $cache = null;

    /** @var PhingFile */
    private $conf = null;

    /** @var string */
    private $executable = "jsl";

    /** @var PhingFile */
    protected $tofile = null;

    /**
     * Sets the flag if warnings should be shown
     *
     * @param boolean $show
     */
    public function setShowWarnings($show)
    {
        $this->showWarnings = StringHelper::booleanValue($show);
    }

    /**
     * The haltonfailure property
     *
     * @param boolean $aValue
     */
    public function setHaltOnFailure($aValue)
    {
        $this->haltOnFailure = $aValue;
    }

    /**
     * The haltonwarning property
     *
     * @param boolean $aValue
     */
    public function setHaltOnWarning($aValue)
    {
        $this->haltOnWarning = $aValue;
    }

    /**
     * File to be performed syntax check on
     *
     * @param PhingFile $file
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * Whether to store last-modified times in cache
     *
     * @param PhingFile $file
     */
    public function setCacheFile(PhingFile $file)
    {
        $this->cache = new DataStore($file);
    }

    /**
     * jsl config file
     *
     * @param PhingFile $file
     */
    public function setConfFile(PhingFile $file)
    {
        $this->conf = $file;
    }

    /**
     * @param string $path
     *
     * @throws BuildException
     */
    public function setExecutable($path)
    {
        $this->executable = $path;

        if (!@file_exists($path)) {
            throw new BuildException("JavaScript Lint executable '{$path}' not found");
        }
    }

    /**
     * @return string
     */
    public function getExecutable()
    {
        return $this->executable;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     *
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * File to save error messages to
     *
     * @param PhingFile $tofile
     */
    public function setToFile(PhingFile $tofile)
    {
        $this->tofile = $tofile;
    }

    /**
     * Execute lint check against PhingFile or a FileSet
     */
    public function main()
    {
        if (!isset($this->file) and count($this->filesets) == 0) {
            throw new BuildException("Missing either a nested fileset or attribute 'file' set");
        }

        if (empty($this->executable)) {
            throw new BuildException("Missing the 'executable' attribute");
        }

        if ($this->file instanceof PhingFile) {
            $this->lint($this->file->getPath());
        } else { // process filesets
            $project = $this->getProject();
            foreach ($this->filesets as $fs) {
                $ds = $fs->getDirectoryScanner($project);
                $files = $ds->getIncludedFiles();
                $dir = $fs->getDir($this->project)->getPath();
                foreach ($files as $file) {
                    $this->lint($dir . DIRECTORY_SEPARATOR . $file);
                }
            }
        }

        // write list of 'bad files' to file (if specified)
        if ($this->tofile) {
            $writer = new FileWriter($this->tofile);

            foreach ($this->badFiles as $file => $messages) {
                foreach ($messages as $msg) {
                    $writer->write($file . "=" . $msg . PHP_EOL);
                }
            }

            $writer->close();
        }

        if ($this->haltOnFailure && $this->hasErrors) {
            throw new BuildException('Syntax error(s) in JS files:' . implode(
                    ', ',
                    array_keys($this->badFiles)
                ));
        }
        if ($this->haltOnWarning && $this->hasWarnings) {
            throw new BuildException('Syntax warning(s) in JS files:' . implode(
                    ', ',
                    array_keys($this->badFiles)
                ));
        }
    }

    /**
     * Performs the actual syntax check
     *
     * @param  string $file
     *
     * @throws BuildException
     *
     * @return bool|void
     */
    protected function lint($file)
    {
        $command = $this->executable . ' -output-format ' . escapeshellarg(
                'file:__FILE__;line:__LINE__;message:__ERROR__'
            ) . ' ';

        if (isset($this->conf)) {
            $command .= '-conf ' . escapeshellarg($this->conf->getPath()) . ' ';
        }

        $command .= '-process ';

        if (file_exists($file)) {
            if (is_readable($file)) {
                if ($this->cache) {
                    $lastmtime = $this->cache->get($file);

                    if ($lastmtime >= filemtime($file)) {
                        $this->log("Not linting '" . $file . "' due to cache", Project::MSG_DEBUG);

                        return false;
                    }
                }

                $messages = array();
                exec($command . '"' . $file . '"', $messages, $return);

                if ($return > 100) {
                    throw new BuildException("Could not execute Javascript Lint executable '{$this->executable}'");
                }

                $summary = $messages[sizeof($messages) - 1];

                preg_match('/(\d+)\serror/', $summary, $matches);
                $errorCount = (count($matches) > 1 ? $matches[1] : 0);

                preg_match('/(\d+)\swarning/', $summary, $matches);
                $warningCount = (count($matches) > 1 ? $matches[1] : 0);

                $errors = array();
                $warnings = array();
                if ($errorCount > 0 || $warningCount > 0) {
                    $last = false;
                    foreach ($messages as $message) {
                        $matches = array();
                        if (preg_match('/^(\.*)\^$/', $message)) {
                            $column = strlen($message);
                            if ($last == 'error') {
                                $errors[count($errors) - 1]['column'] = $column;
                            } else {
                                if ($last == 'warning') {
                                    $warnings[count($warnings) - 1]['column'] = $column;
                                }
                            }
                            $last = false;
                        }
                        if (!preg_match('/^file:(.+);line:(\d+);message:(.+)$/', $message, $matches)) {
                            continue;
                        }
                        $msg = $matches[3];
                        $data = array('filename' => $matches[1], 'line' => $matches[2], 'message' => $msg);
                        if (preg_match('/^.*error:.+$/i', $msg)) {
                            $errors[] = $data;
                            $last = 'error';
                        } else {
                            if (preg_match('/^.*warning:.+$/i', $msg)) {
                                $warnings[] = $data;
                                $last = 'warning';
                            }
                        }
                    }
                }

                if ($this->showWarnings && $warningCount > 0) {
                    $this->log($file . ': ' . $warningCount . ' warnings detected', Project::MSG_WARN);
                    foreach ($warnings as $warning) {
                        $this->log(
                            '- line ' . $warning['line'] . (isset($warning['column']) ? ' column ' . $warning['column'] : '') . ': ' . $warning['message'],
                            Project::MSG_WARN
                        );
                    }
                    $this->hasWarnings = true;
                }

                if ($errorCount > 0) {
                    $this->log($file . ': ' . $errorCount . ' errors detected', Project::MSG_ERR);
                    if (!isset($this->badFiles[$file])) {
                        $this->badFiles[$file] = array();
                    }

                    foreach ($errors as $error) {
                        $message = 'line ' . $error['line'] . (isset($error['column']) ? ' column ' . $error['column'] : '') . ': ' . $error['message'];
                        $this->log('- ' . $message, Project::MSG_ERR);
                        array_push($this->badFiles[$file], $message);
                    }
                    $this->hasErrors = true;
                } else {
                    if (!$this->showWarnings || $warningCount == 0) {
                        $this->log($file . ': No syntax errors detected', Project::MSG_VERBOSE);

                        if ($this->cache) {
                            $this->cache->put($file, filemtime($file));
                        }
                    }
                }
            } else {
                throw new BuildException('Permission denied: ' . $file);
            }
        } else {
            throw new BuildException('File not found: ' . $file);
        }
    }
}
<?php
/*
 *  $Id: 5581c575662a85f7e1b8c80af2291e74a3aca3f9 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Task to minify javascript files.
 *
 * Requires JShrink (https://github.com/tedivm/JShrink) which
 * can be installed using composer
 *
 * @author Frank Kleine <mikey@stubbles.net>
 * @version $Id: 5581c575662a85f7e1b8c80af2291e74a3aca3f9 $
 * @package phing.tasks.ext
 * @since 2.3.0
 */
class JsMinTask extends Task
{
    /**
     * the source files
     *
     * @var  FileSet
     */
    protected $filesets = array();
    /**
     * Whether the build should fail, if
     * errors occurred
     *
     * @var boolean
     */
    protected $failonerror = false;

    /**
     * Define if the target should use or not a suffix -min
     *
     * @var boolean
     */
    protected $suffix = '-min';

    /**
     * directory to put minified javascript files into
     *
     * @var  string
     */
    protected $targetDir = "";

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Whether the build should fail, if an error occurred.
     *
     * @param boolean $value
     */
    public function setFailonerror($value)
    {
        $this->failonerror = $value;
    }

    /**
     * Define if the task should or not use a suffix (-min is the default)
     *
     * @param string $value
     */
    public function setSuffix($value)
    {
        $this->suffix = $value;
    }

    /**
     * sets the directory where minified javascript files should be put into
     *
     * @param string $targetDir
     */
    public function setTargetDir($targetDir)
    {
        $this->targetDir = $targetDir;
    }

    /**
     * The init method: Do init steps.
     */
    public function init()
    {
        return true;
    }

    /**
     * The main entry point method.
     */
    public function main()
    {
        // if composer autoloader is not yet loaded, load it here
        @include_once 'vendor/autoload.php';
        if (!class_exists('\\JShrink\\Minifier')) {
            throw new BuildException(
                'JsMinTask depends on JShrink being installed and on include_path.',
                $this->getLocation()
            );
        }

        if (version_compare(PHP_VERSION, '5.3.0') < 0) {
            throw new BuildException('JsMinTask requires PHP 5.3+');
        }

        if (empty($this->targetDir)) {
            throw new BuildException('Attribute "targetDir" is required');
        }

        foreach ($this->filesets as $fs) {
            try {
                $this->processFileSet($fs);
            } catch (BuildException $be) {
                // directory doesn't exist or is not readable
                if ($this->failonerror) {
                    throw $be;
                } else {
                    $this->log($be->getMessage(), $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
                }
            }
        }
    }

    /**
     * @param FileSet $fs
     * @throws BuildException
     */
    protected function processFileSet(FileSet $fs)
    {
        $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles();
        $fullPath = realpath($fs->getDir($this->project));
        foreach ($files as $file) {
            $this->log('Minifying file ' . $file);
            try {
                $target = $this->targetDir . '/' . str_replace(
                        $fullPath,
                        '',
                        str_replace('.js', $this->suffix . '.js', $file)
                    );
                if (file_exists(dirname($target)) === false) {
                    mkdir(dirname($target), 0777 - umask(), true);
                }

                $contents = file_get_contents($fullPath . '/' . $file);

                // nasty hack to not trip PHP 5.2 parser
                $minified = forward_static_call(array('\\JShrink\\Minifier', 'minify'), $contents);

                file_put_contents($target, $minified);
            } catch (Exception $jsme) {
                $this->log("Could not minify file $file: " . $jsme->getMessage(), Project::MSG_ERR);
            }
        }
    }
}
<?php

/**
 * Copyright (c) 2007-2011 bitExpert AG
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/system/ExecTask.php';

/**
 * Abstract Liquibase task. Base class for all Liquibase Phing tasks.
 *
 * @author Stephan Hochdoerfer <S.Hochdoerfer@bitExpert.de>
 * @version $Id: e60f3f6a7b9c03e72b55f514ab85f2e8bb1e3f81 $
 * @since 2.4.10
 * @package phing.tasks.ext.liquibase
 */
abstract class AbstractLiquibaseTask extends Task
{

    /**
     * Used for liquibase -Dname=value properties.
     */
    private $properties = array();

    /**
     * Used to set liquibase --name=value parameters
     */
    private $parameters = array();

    protected $jar;
    protected $changeLogFile;
    protected $username;
    protected $password;
    protected $url;
    protected $classpathref;

    /**
     * Whether to display the output of the command.
     * True by default to preserve old behaviour
     * @var boolean
     */
    protected $display = true;

    /**
     * Whether liquibase return code can cause a Phing failure.
     * @var boolean
     */
    protected $checkreturn = false;

    /**
     * Set true if we should run liquibase with PHP passthru
     * instead of exec.
     */
    protected $passthru = true;

    /**
     * Property name to set with output value from exec call.
     *
     * @var string
     */
    protected $outputProperty;

    /**
     * Sets the absolute path to liquibase jar.
     *
     * @param string the absolute path to the liquibase jar.
     */
    public function setJar($jar)
    {
        $this->jar = $jar;
    }

    /**
     * Sets the absolute path to the changelog file to use.
     *
     * @param string the absolute path to the changelog file
     */
    public function setChangeLogFile($changelogFile)
    {
        $this->changeLogFile = $changelogFile;
    }

    /**
     * Sets the username to connect to the database.
     *
     * @param string the username
     */
    public function setUsername($username)
    {
        $this->username = $username;
    }

    /**
     * Sets the password to connect to the database.
     *
     * @param string the password
     */
    public function setPassword($password)
    {
        $this->password = $password;
    }

    /**
     * Sets the url to connect to the database in jdbc style, e.g.
     * <code>
     * jdbc:postgresql://psqlhost/mydatabase
     * </code>
     *
     * @param string jdbc connection string
     */
    public function setUrl($url)
    {
        $this->url = $url;
    }

    /**
     * Sets the Java classpathref.
     *
     * @param string A reference to the classpath that contains the database
     *                    driver, liquibase.jar, and the changelog.xml file
     */
    public function setclasspathref($classpathref)
    {
        $this->classpathref = $classpathref;
    }

    /**
     * Sets whether to display the output of the command
     * @param boolean $display
     */
    public function setDisplay($display)
    {
        $this->display = StringHelper::booleanValue($display);
    }

    /**
     * Whether to check the liquibase return code.
     *
     * @param boolean $checkreturn
     */
    public function setCheckreturn($checkreturn)
    {
        $this->checkreturn = StringHelper::booleanValue($checkreturn);
    }

    /**
     * Whether to check the liquibase return code.
     *
     * @param $passthru
     * @internal param bool $checkreturn
     */
    public function setPassthru($passthru)
    {
        $this->passthru = StringHelper::booleanValue($passthru);
    }

    /**
     * the name of property to set to output value from exec() call.
     *
     * @param string $prop property name
     *
     * @return void
     */
    public function setOutputProperty($prop)
    {
        $this->outputProperty = $prop;
    }

    /**
     * Creates a nested <property> tag.
     *
     * @return LiquibaseProperty Argument object
     */
    public function createProperty()
    {
        $prop = new LiquibaseProperty();
        $this->properties[] = $prop;

        return $prop;
    }

    /**
     * Creates a nested <parameter> tag.
     *
     * @return LiquibaseParameter Argument object
     */
    public function createParameter()
    {
        $param = new LiquibaseParameter();
        $this->parameters[] = $param;

        return $param;
    }

    /**
     * Ensure that correct parameters were passed in.
     *
     * @throws BuildException
     * @return void
     */
    protected function checkParams()
    {
        if ((null === $this->jar) or !file_exists($this->jar)) {
            throw new BuildException(
                sprintf(
                    'Specify the name of the LiquiBase.jar. "%s" does not exist!',
                    $this->jar
                )
            );
        }

        $this->checkChangeLogFile();

        if (null === $this->classpathref) {
            throw new BuildException('Please provide a classpath!');
        }

        if (null === $this->username) {
            throw new BuildException('Please provide a username for database acccess!');
        }

        if (null === $this->password) {
            throw new BuildException('Please provide a password for database acccess!');
        }

        if (null === $this->url) {
            throw new BuildException('Please provide a url for database acccess!');
        }
    }

    /**
     * Executes the given command and returns the output.
     *
     * @param $lbcommand
     * @param string $lbparams the command to execute
     * @throws BuildException
     * @return string the output of the executed command
     */
    protected function execute($lbcommand, $lbparams = '')
    {
        $nestedparams = "";
        foreach ($this->parameters as $p) {
            $nestedparams .= $p->getCommandline($this->project) . ' ';
        }
        $nestedprops = "";
        foreach ($this->properties as $p) {
            $nestedprops .= $p->getCommandline($this->project) . ' ';
        }

        $command = sprintf(
            'java -jar %s --changeLogFile=%s --url=%s --username=%s --password=%s --classpath=%s %s %s %s %s 2>&1',
            escapeshellarg($this->jar),
            escapeshellarg($this->changeLogFile),
            escapeshellarg($this->url),
            escapeshellarg($this->username),
            escapeshellarg($this->password),
            escapeshellarg($this->classpathref),
            $nestedparams,
            escapeshellarg($lbcommand),
            $lbparams,
            $nestedprops
        );

        if ($this->passthru) {
            passthru($command);
        } else {
            $output = array();
            $return = null;
            exec($command, $output, $return);
            $output = implode(PHP_EOL, $output);

            if ($this->display) {
                print $output;
            }

            if (!empty($this->outputProperty)) {
                $this->project->setProperty($this->outputProperty, $output);
            }

            if ($this->checkreturn && $return != 0) {
                throw new BuildException("Liquibase exited with code $return");
            }
        }

        return;
    }

    protected function checkChangeLogFile()
    {
        if (null === $this->changeLogFile) {
            throw new BuildException('Specify the name of the changelog file.');
        }

        foreach (explode(":", $this->classpathref) as $path) {
            if (file_exists($path . DIRECTORY_SEPARATOR . $this->changeLogFile)) {
                return;
            }
        }

        if (!file_exists($this->changeLogFile)) {
            throw new BuildException(
                sprintf(
                    'The changelog file "%s" does not exist!',
                    $this->changeLogFile
                )
            );
        }
    }
}

/**
 * @author Stephan Hochdoerfer <S.Hochdoerfer@bitExpert.de>
 * @version $Id: e60f3f6a7b9c03e72b55f514ab85f2e8bb1e3f81 $
 * @since 2.4.10
 * @package phing.tasks.ext.liquibase
 */
class LiquibaseParameter extends DataType
{
    private $name;
    private $value;

    /**
     * @param $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * @param $value
     */
    public function setValue($value)
    {
        $this->value = $value;
    }

    /**
     * @param Project $p
     * @return string
     * @throws BuildException
     */
    public function getCommandline(Project $p)
    {
        if ($this->isReference()) {
            return $this->getRef($p)->getCommandline($p);
        }

        return sprintf("--%s=%s", $this->name, escapeshellarg($this->value));
    }

    /**
     * @param Project $p
     * @return mixed
     * @throws BuildException
     */
    public function getRef(Project $p)
    {
        if (!$this->checked) {
            $stk = array();
            array_push($stk, $this);
            $this->dieOnCircularReference($stk, $p);
        }

        $o = $this->ref->getReferencedObject($p);
        if (!($o instanceof LiquibaseParameter)) {
            throw new BuildException($this->ref->getRefId() . " doesn't denote a LiquibaseParameter");
        } else {
            return $o;
        }
    }

}

/**
 * @author Stephan Hochdoerfer <S.Hochdoerfer@bitExpert.de>
 * @version $Id: e60f3f6a7b9c03e72b55f514ab85f2e8bb1e3f81 $
 * @since 2.4.10
 * @package phing.tasks.ext.liquibase
 */
class LiquibaseProperty extends DataType
{
    private $name;
    private $value;

    /**
     * @param $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * @param $value
     */
    public function setValue($value)
    {
        $this->value = $value;
    }

    /**
     * @param Project $p
     * @return string
     * @throws BuildException
     */
    public function getCommandline(Project $p)
    {
        if ($this->isReference()) {
            return $this->getRef($p)->getCommandline($p);
        }

        return sprintf("-D%s=%s", $this->name, escapeshellarg($this->value));
    }

    /**
     * @param Project $p
     * @return mixed
     * @throws BuildException
     */
    public function getRef(Project $p)
    {
        if (!$this->checked) {
            $stk = array();
            array_push($stk, $this);
            $this->dieOnCircularReference($stk, $p);
        }
        $o = $this->ref->getReferencedObject($p);
        if (!($o instanceof LiquibaseProperty)) {
            throw new BuildException($this->ref->getRefId() . " doesn't denote a LiquibaseProperty");
        } else {
            return $o;
        }
    }
}
<?php

/**
 * Copyright (c) 2007-2011 bitExpert AG
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

require_once 'phing/tasks/ext/liquibase/AbstractLiquibaseTask.php';

/**
 * Task to create a changelog file.
 *
 * @author Stephan Hochdoerfer <S.Hochdoerfer@bitExpert.de>
 * @version $Id: 5d14d92ab948ab412f1b18b54189278f939a34d5 $
 * @since 2.4.10
 * @package phing.tasks.ext.liquibase
 */
class LiquibaseChangeLogTask extends AbstractLiquibaseTask
{
    /**
     * @see Task::main()
     */
    public function main()
    {
        $this->checkParams();
        $this->execute('generateChangeLog');
    }
}
<?php

/**
 * Copyright (c) 2007-2011 bitExpert AG
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

require_once 'phing/tasks/ext/liquibase/AbstractLiquibaseTask.php';

/**
 * Task to create a javadoc-like documentation based on current database and
 * changelog.
 *
 * @author Stephan Hochdoerfer <S.Hochdoerfer@bitExpert.de>
 * @version $Id: a8df74bfdcd6fceb45725bbaa7076682223fc8b1 $
 * @since 2.4.10
 * @package phing.tasks.ext.liquibase
 */
class LiquibaseDbDocTask extends AbstractLiquibaseTask
{
    protected $outputDir;

    /**
     * Sets the output directory where the documentation gets generated to.
     *
     * @param string the output directory
     */
    public function setOutputDir($outputDir)
    {
        $this->outputDir = $outputDir;
    }

    /**
     * @see AbstractTask::checkParams()
     */
    protected function checkParams()
    {
        parent::checkParams();

        if ((null === $this->outputDir) or !is_dir($this->outputDir)) {
            if (!mkdir($this->outputDir, 0777, true)) {
                throw new BuildException(
                    sprintf(
                        'The directory "%s" does not exist and could not be created!',
                        $this->outputDir
                    )
                );
            }
        }

        if (!is_writable($this->outputDir)) {
            throw new BuildException(
                sprintf(
                    'The directory "%s" is not writable!',
                    $this->outputDir
                )
            );
        }
    }

    /**
     * @see Task::main()
     */
    public function main()
    {
        $this->checkParams();
        $this->execute('dbdoc', escapeshellarg($this->outputDir));
    }
}
<?php

/**
 * Copyright (c) 2007-2011 bitExpert AG
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

require_once 'phing/tasks/ext/liquibase/AbstractLiquibaseTask.php';

/**
 * Task to create the diff between two databases. Will output the changes needed
 * to convert the reference database to the database.
 *
 * @author Stephan Hochdoerfer <S.Hochdoerfer@bitExpert.de>
 * @version $Id: d678839b8d9ff66611eac44926d340bc8fe97128 $
 * @since 2.4.10
 * @package phing.tasks.ext.liquibase
 */
class LiquibaseDiffTask extends AbstractLiquibaseTask
{
    protected $referenceUsername;
    protected $referencePassword;
    protected $referenceUrl;

    /**
     * Sets the username to connect to the reference database.
     *
     * @param string the username
     */
    public function setReferenceUsername($username)
    {
        $this->referenceUsername = $username;
    }

    /**
     * Sets the password to connect to the reference database.
     *
     * @param string the password
     */
    public function setReferencePassword($password)
    {
        $this->referencePassword = $password;
    }

    /**
     * Sets the url to connect to the reference database in jdbc style, e.g.
     * <code>
     * jdbc:postgresql://psqlhost/myrefdatabase
     * </code>
     *
     * @param string jdbc connection string
     */
    public function setReferenceUrl($url)
    {
        $this->referenceUrl = $url;
    }

    /**
     * @see AbstractTask::checkParams()
     */
    protected function checkParams()
    {
        parent::checkParams();

        if (null === $this->referenceUsername) {
            throw new BuildException('Please provide a username for the reference database acccess!');
        }

        if (null === $this->referencePassword) {
            throw new BuildException('Please provide a password for the reference database acccess!');
        }

        if (null === $this->referenceUrl) {
            throw new BuildException('Please provide a url for the reference database acccess!');
        }
    }

    /**
     * @see Task::main()
     */
    public function main()
    {
        $this->checkParams();

        $refparams = sprintf(
            '--referenceUsername=%s --referencePassword=%s --referenceUrl=%s',
            escapeshellarg($this->referenceUsername),
            escapeshellarg($this->referencePassword),
            escapeshellarg($this->referenceUrl)
        );

        // save main changelog file
        $changelogFile = $this->changeLogFile;

        // set the name of the new generated changelog file
        $this->setChangeLogFile(dirname($changelogFile) . '/diffs/' . date('YmdHis') . '.xml');
        if (!is_dir(dirname($changelogFile) . '/diffs/')) {
            mkdir(dirname($changelogFile) . '/diffs/', 0777, true);
        }
        $this->execute('diffChangeLog', $refparams);

        $xmlFile = new DOMDocument();
        $xmlFile->load($changelogFile);

        // create the new node
        $rootNode = $xmlFile->getElementsByTagName('databaseChangeLog')->item(0);
        $includeNode = $rootNode->appendChild($xmlFile->createElement('include'));

        // set the attributes for the new node
        $includeNode->setAttribute('file', str_replace(dirname($changelogFile) . '/', '', $this->changeLogFile));
        $includeNode->setAttribute('relativeToChangelogFile', 'true');
        file_put_contents($changelogFile, $xmlFile->saveXML());

        $this->setChangeLogFile($changelogFile);
        $this->execute('markNextChangeSetRan');
    }
}
<?php

/**
 * Copyright (c) 2007-2011 bitExpert AG
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

require_once 'phing/tasks/ext/liquibase/AbstractLiquibaseTask.php';

/**
 * Rollbacks the database changes.
 *
 * @author Stephan Hochdoerfer <S.Hochdoerfer@bitExpert.de>
 * @version $Id: 1915fb3b50b9c646643a44df4cf4457a2c79f6f1 $
 * @since 2.4.10
 * @package phing.tasks.ext.liquibase
 */
class LiquibaseRollbackTask extends AbstractLiquibaseTask
{
    protected $rollbackTag;

    /**
     * Sets the name of the tag to roll back to.
     *
     * @param string the name to roll back to
     */
    public function setRollbackTag($rollbackTag)
    {
        $this->rollbackTag = $rollbackTag;
    }

    /**
     * @see AbstractTask::checkParams()
     */
    protected function checkParams()
    {
        parent::checkParams();

        if (null === $this->rollbackTag) {
            throw new BuildException(
                sprintf(
                    'Please specify the tag to rollback to!',
                    $this->rollbackTag
                )
            );
        }
    }

    /**
     * @see Task::main()
     */
    public function main()
    {
        $this->checkParams();
        $this->execute('rollback', escapeshellarg($this->rollbackTag));
    }
}
<?php

/**
 * Copyright (c) 2007-2011 bitExpert AG
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

require_once 'phing/tasks/ext/liquibase/AbstractLiquibaseTask.php';

/**
 * Task to tag the current database state. In case you tag the database multiple
 * times without applying a new changelog before, the tags will overwrite each
 * other!
 *
 * @author Stephan Hochdoerfer <S.Hochdoerfer@bitExpert.de>
 * @version $Id: 1a967314c731282d7e026e0e8670b003eaab9802 $
 * @since 2.4.10
 * @package phing.tasks.ext.liquibase
 */
class LiquibaseTagTask extends AbstractLiquibaseTask
{
    protected $tag;

    /**
     * Sets the name of tag which is used to mark the database state for
     * possible future rollback.
     *
     * @param string the name to tag the database with
     */
    public function setTag($tag)
    {
        $this->tag = $tag;
    }

    /**
     * @see AbstractTask::checkParams()
     */
    protected function checkParams()
    {
        parent::checkParams();

        if (null === $this->tag) {
            throw new BuildException(
                sprintf(
                    'Please specify the tag!',
                    $this->tag
                )
            );
        }
    }

    /**
     * @see Task::main()
     */
    public function main()
    {
        $this->checkParams();
        $this->execute('tag', escapeshellarg($this->tag));
    }
}
<?php
/*
 * $Id: c492370d37df91fd100b00fd3a7c1ba7f628b0e1 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/liquibase/AbstractLiquibaseTask.php';

/**
 * Task for running liquibase commands that doesn't have their own
 * commands yet.
 *
 * Parameters can be provided by nested <parameter name='foo' value='bar' /> tags.
 * That will result in --foo='bar' on the command line.
 *
 * @author Joakim Israelsson <joakim.israelsson.86@gmail.com>
 * @version $Id: c492370d37df91fd100b00fd3a7c1ba7f628b0e1 $
 * @package phing.tasks.ext.liquibase
 */
class LiquibaseTask extends AbstractLiquibaseTask
{

    /**
     * What liquibase command you wish to run.
     */
    private $command;

    /**
     * @param $command
     */
    public function setCommand($command)
    {
        $this->command = (string) $command;
    }

    protected function checkParams()
    {
        parent::checkParams();

        if (null === $this->command) {
            throw new BuildException('Please provide a liquibase command.');
        }
    }

    public function main()
    {
        $this->checkParams();
        $this->execute($this->command, '');
    }

}
<?php

/**
 * Copyright (c) 2007-2011 bitExpert AG
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

require_once 'phing/tasks/ext/liquibase/AbstractLiquibaseTask.php';

/**
 * Task to update the database to latest version of the changelog file.
 *
 * @author Stephan Hochdoerfer <S.Hochdoerfer@bitExpert.de>
 * @version $Id: 9bc3829efb98aff78729b2da69cfde7d9c1204ff $
 * @since 2.4.10
 * @package phing.tasks.ext.liquibase
 */
class LiquibaseUpdateTask extends AbstractLiquibaseTask
{
    /**
     * @see Task::main()
     */
    public function main()
    {
        $this->checkParams();
        $this->execute('update');
    }
}
<?php
/*
 *  $Id: 6e8a0340847844261a75b7b0eb37ad92e58e1f9b $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/Task.php';

/**
 * Send an e-mail message
 *
 * <mail tolist="user@example.org" subject="build complete">The build process is a success...</mail>
 *
 * @author   Michiel Rook <mrook@php.net>
 * @author   Francois Harvey at SecuriWeb (http://www.securiweb.net)
 * @version  $Id: 6e8a0340847844261a75b7b0eb37ad92e58e1f9b $
 * @package  phing.tasks.ext
 */
class MailTask extends Task
{
    protected $tolist = null;
    protected $subject = null;
    protected $msg = null;
    protected $from = null;

    protected $filesets = array();

    protected $backend = 'mail';
    protected $backendParams = array();

    public function main()
    {
        if (empty($this->from)) {
            throw new BuildException('Missing "from" attribute');
        }

        $this->log('Sending mail to ' . $this->tolist);

        if (!empty($this->filesets)) {
            $this->sendFilesets();

            return;
        }

        mail($this->tolist, $this->subject, $this->msg, "From: {$this->from}\n");
    }

    protected function sendFilesets()
    {
        @require_once 'Mail.php';
        @require_once 'Mail/mime.php';

        if (!class_exists('Mail_mime')) {
            throw new BuildException('Need the PEAR Mail_mime package to send attachments');
        }

        $mime = new Mail_mime(array('text_charset' => 'UTF-8'));
        $hdrs = array(
            'From' => $this->from,
            'Subject' => $this->subject
        );
        $mime->setTXTBody($this->msg);

        foreach ($this->filesets as $fs) {
            $ds = $fs->getDirectoryScanner($this->project);
            $fromDir = $fs->getDir($this->project);
            $srcFiles = $ds->getIncludedFiles();

            foreach ($srcFiles as $file) {
                $mime->addAttachment($fromDir . DIRECTORY_SEPARATOR . $file, 'application/octet-stream');
            }
        }

        $body = $mime->get();
        $hdrs = $mime->headers($hdrs);

        $mail = Mail::factory($this->backend, $this->backendParams);
        $mail->send($this->tolist, $hdrs, $body);
    }

    /**
     * Setter for message
     * @param $msg
     */
    public function setMsg($msg)
    {
        $this->setMessage($msg);
    }

    /**
     * Alias setter
     * @param $msg
     */
    public function setMessage($msg)
    {
        $this->msg = (string) $msg;
    }

    /**
     * Setter for subject
     * @param $subject
     */
    public function setSubject($subject)
    {
        $this->subject = (string) $subject;
    }

    /**
     * Setter for tolist
     * @param $tolist
     */
    public function setToList($tolist)
    {
        $this->tolist = $tolist;
    }

    /**
     * Alias for (deprecated) recipient
     * @param $recipient
     */
    public function setRecipient($recipient)
    {
        $this->tolist = (string) $recipient;
    }

    /**
     * Alias for to
     * @param $to
     */
    public function setTo($to)
    {
        $this->tolist = (string) $to;
    }

    /**
     * Supports the <mail>Message</mail> syntax.
     * @param $msg
     */
    public function addText($msg)
    {
        $this->msg = (string) $msg;
    }

    /**
     * Sets email address of sender
     * @param $from
     */
    public function setFrom($from)
    {
        $this->from = $from;
    }

    /**
     * Sets PEAR Mail backend to use
     * @param $backend
     */
    public function setBackend($backend)
    {
        $this->backend = $backend;
    }

    /**
     * Sets PEAR Mail backend params to use
     * @param $backendParams
     */
    public function setBackendParams($backendParams)
    {
        $params = explode(',', $backendParams);

        foreach ($params as $param) {
            $values = explode('=', $param);

            if (count($values) < 1) {
                continue;
            }

            if (count($values) == 1) {
                $this->backendParams[] = $values[0];
            } else {
                $key = $values[0];
                $value = $values[1];
                $this->backendParams[$key] = $value;
            }
        }
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }
}
<?php
/**
 * $Id: b8d07ffcebc0e254a037a4351394cf6d124f8d97 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once "phing/Task.php";
require_once 'phing/system/io/PhingFile.php';

/**
 * ManifestTask
 *
 * Generates a simple Manifest file with optional checksums.
 *
 *
 * Manifest schema:
 * ...
 * path/to/file     CHECKSUM    [CHECKSUM2]     [CHECKSUM3]
 * path/to/secondfile       CHECKSUM    [CHECKSUM2]     [CHECKSUM3]
 * ...
 *
 * Example usage:
 * <manifest checksum="crc32" file="${dir_build}/Manifest">
 *      <fileset refid="files_build" />
 * </manifest>
 *
 * <manifest checksum="md5,adler32,sha256" file="${dir_build}/Manifest">
 *      <fileset refid="files_build" />
 * </manifest>
 *
 *
 *
 * @author David Persson <davidpersson at qeweurope dot org>
 *
 * @package phing.tasks.ext
 *
 * @since 2.3.1
 */
class ManifestTask extends Task
{
    public $taskname = 'manifest';

    /**
     * Action
     *
     * "w" for reading in files from fileSet
     * and writing manifest
     *
     * or
     *
     * "r" for reading in files from fileSet
     * and checking against manifest
     *
     * @var string "r" or "w"
     */
    private $action = 'w';

    /**
     * The target file passed in the buildfile.
     */
    private $destFile = null;

    /**
     * Holds filesets
     *
     * @var array An Array of objects
     */
    private $filesets = array();

    /**
     * Enable/Disable checksuming or/and select algorithm
     * true defaults to md5
     * false disables checksuming
     * string "md5,sha256,..." enables generation of multiple checksums
     * string "sha256" generates sha256 checksum only
     *
     * @var bool|string
     */
    private $checksum = false;

    /**
     * A string used in hashing method
     *
     * @var string
     */
    private $salt = '';

    /**
     * Holds some data collected during runtime
     *
     * @var array
     */
    private $meta = array('totalFileCount' => 0, 'totalFileSize' => 0);

    /**
     * The setter for the attribute "file".
     * This is where the manifest will be written to/read from
     *
     * @param PhingFile $file Path to readable file
     *
     * @return void
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * The setter for the attribute "checksum"
     *
     * @param  mixed $mixed
     *
     * @return void
     */
    public function setChecksum($mixed)
    {
        if (is_string($mixed)) {
            $data = array(strtolower($mixed));

            if (strpos($data[0], ',')) {
                $data = explode(',', $mixed);
            }

            $this->checksum = $data;

        } elseif ($mixed === true) {
            $this->checksum = array('md5');
        }
    }

    /**
     * The setter for the optional attribute "salt"
     *
     * @param  string $string
     *
     * @return void
     */
    public function setSalt($string)
    {
        $this->salt = $string;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     *
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * The init method: Do init steps.
     *
     * {@inheritdoc}
     *
     * @internal nothing to do here
     */
    public function init()
    {
    }

    /**
     * Delegate the work.
     *
     * {@inheritdoc}
     */
    public function main()
    {
        $this->validateAttributes();

        if ($this->action == 'w') {
            $this->write();

        } elseif ($this->action == 'r') {
            $this->read();

        }
    }

    /**
     * Creates Manifest file
     * Writes to $this->file
     *
     * @throws BuildException
     */
    private function write()
    {
        $project = $this->getProject();

        if (!touch($this->file->getPath())) {
            throw new BuildException("Unable to write to " . $this->file->getPath() . ".");
        }

        $this->log("Writing to " . $this->file->__toString(), Project::MSG_INFO);

        if (is_array($this->checksum)) {
            $this->log("Using " . implode(', ', $this->checksum) . " for checksuming.", Project::MSG_INFO);
        }

        foreach ($this->filesets as $fs) {

            $dir = $fs->getDir($this->project)->getPath();

            $ds = $fs->getDirectoryScanner($project);
            $fromDir = $fs->getDir($project);
            $srcFiles = $ds->getIncludedFiles();
            $srcDirs = $ds->getIncludedDirectories();

            foreach ($ds->getIncludedFiles() as $file_path) {
                $line = $file_path;
                if ($this->checksum) {
                    foreach ($this->checksum as $algo) {
                        if (!$hash = $this->hashFile($dir . '/' . $file_path, $algo)) {
                            throw new BuildException("Hashing $dir/$file_path with $algo failed!");
                        }

                        $line .= "\t" . $hash;
                    }
                }
                $line .= "\n";
                $manifest[] = $line;
                $this->log("Adding file " . $file_path, Project::MSG_VERBOSE);
                $this->meta['totalFileCount']++;
                $this->meta['totalFileSize'] += filesize($dir . '/' . $file_path);
            }

        }

        file_put_contents($this->file, $manifest);

        $this->log(
            "Done. Total files: " . $this->meta['totalFileCount'] . ". Total file size: " . $this->meta['totalFileSize'] . " bytes.",
            Project::MSG_INFO
        );
    }

    /**
     * @todo implement
     */
    private function read()
    {
        throw new BuildException("Checking against manifest not yet supported.");
    }

    /**
     * Wrapper method for hash generation
     * Automatically selects extension
     * Falls back to built-in functions
     *
     * @link  http://www.php.net/mhash
     * @link  http://www.php.net/hash
     *
     * @param  string $msg  The string that should be hashed
     * @param  string $algo Algorithm
     *
     * @return mixed  String on success, false if $algo is not available
     */
    private function hash($msg, $algo)
    {
        if (extension_loaded('hash')) {
            $algo = strtolower($algo);

            if (in_array($algo, hash_algos())) {
                return hash($algo, $this->salt . $msg);
            }

        }

        if (extension_loaded('mhash')) {
            $algo = strtoupper($algo);

            if (defined('MHASH_' . $algo)) {
                return mhash('MHASH_' . $algo, $this->salt . $msg);

            }
        }

        switch (strtolower($algo)) {
            case 'md5':
                return md5($this->salt . $msg);
            case 'crc32':
                return abs(crc32($this->salt . $msg));
        }

        return false;
    }

    /**
     * Hash a file's contents
     *
     * @param  string $file
     * @param  string $algo
     *
     * @return mixed  String on success, false if $algo is not available
     */
    private function hashFile($file, $algo)
    {
        if (!file_exists($file)) {
            return false;
        }

        $msg = file_get_contents($file);

        return $this->hash($msg, $algo);
    }

    /**
     * Validates attributes coming in from XML
     *
     * @return void
     *
     * @throws BuildException
     */
    protected function validateAttributes()
    {
        if ($this->action != 'r' && $this->action != 'w') {
            throw new BuildException("'action' attribute has non valid value. Use 'r' or 'w'");
        }

        if (empty($this->salt)) {
            $this->log("No salt provided. Specify one with the 'salt' attribute.", Project::MSG_WARN);
        }

        if (is_null($this->file) && count($this->filesets) === 0) {
            throw new BuildException("Specify at least sources and destination - a file or a fileset.");
        }

        if (!is_null($this->file) && $this->file->exists() && $this->file->isDirectory()) {
            throw new BuildException("Destination file cannot be a directory.");
        }
    }
}
<?php
/**
 * Utilise notify-send from within Phing.
 *
 * PHP Version 5
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <ken@linux.ie>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     https://github.com/kenguest/Phing-NotifySendTask
 */

require_once 'phing/Task.php';

/**
 * NotifySendTask
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Ken Guest <ken@linux.ie>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @link     NotifySendTask.php
 */
class NotifySendTask extends Task
{
    protected $msg = null;
    protected $title = null;
    protected $icon = 'info';
    protected $silent = false;

    /**
     * Set icon attribute
     *
     * @param string $icon name/location of icon
     *
     * @return void
     */
    public function setIcon($icon)
    {
        switch ($icon)
        {
        case 'info':
        case 'error':
        case 'warning':
            $this->icon = $icon;
            break;
        default:
            if (file_exists($icon) && is_file($icon)) {
                $this->icon = $icon;
            } else {
                if (isset($this->log)) {
                    $this->log(
                        sprintf(
                            "%s is not a file. Using default icon instead.",
                            $icon
                        ),
                        Project::MSG_WARN
                    );
                }
            }
        }
    }

    /**
     * Get icon to be used (filename or generic name)
     *
     * @return string
     */
    public function getIcon()
    {
        return $this->icon;
    }

    /**
     * Set to a true value to not execute notifysend command.
     *
     * @param bool $silent Don't execute notifysend? True not to.
     *
     * @return void
     */
    public function setSilent($silent)
    {
        $this->silent = (bool) $silent;
    }

    /**
     * Set title attribute
     *
     * @param string $title Title to display
     *
     * @return void
     */
    public function setTitle($title)
    {
        $this->title = $title;
    }

    /**
     * Get Title
     *
     * @return string
     */
    public function getTitle()
    {
        return $this->title;
    }

    /**
     * Set msg attribute
     *
     * @param string $msg Message
     *
     * @return void
     */
    public function setMsg($msg)
    {
        $this->msg = $msg;
    }

    /**
     * Get message.
     *
     * @return string
     */
    public function getMsg()
    {
        return $this->msg;
    }

    /**
     * The main entry point method.
     *
     * @throws BuildException
     * @return void
     */
    public function main()
    {
        $msg = '';
        $title = 'Phing';

        if ($this->title != '') {
            $title = "'" . $this->title . "'";
        }

        if ($this->msg != '') {
            $msg = "'" . $this->msg . "'";
        }

        $cmd = 'notify-send -i ' . $this->icon . ' ' . $title . ' ' . $msg;

        $this->log(sprintf("cmd: %s", $cmd), Project::MSG_DEBUG);
        if (!$this->silent) {
            exec(escapeshellcmd($cmd), $output, $return);
            if ($return !== 0) {
                throw new BuildException("Notify task failed.");
            }
        }
        $this->log(sprintf("Title: '%s'", $title), Project::MSG_DEBUG);
        $this->log(sprintf("Message: '%s'", $msg), Project::MSG_DEBUG);
        $this->log($msg, Project::MSG_INFO);
    }
}

// vim:set et ts=4 sw=4:
<?php

/*
 *  $Id: 7fee76167437d1ca7bdca2fef13aafc08811085f $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Convert dot-notation packages to relative paths.
 *
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: 7fee76167437d1ca7bdca2fef13aafc08811085f $
 * @package   phing.tasks.ext
 */
class PackageAsPathTask extends Task
{

    /** The package to convert. */
    protected $pckg;

    /** The property to store the conversion in. */
    protected $name;

    /**
     * Executes the package to patch converstion and stores it
     * in the user property <code>name</code>.
     */
    public function main()
    {
        $this->project->setUserProperty($this->name, strtr($this->pckg, '.', '/'));
    }

    /**
     * @param string $pckg the package to convert
     */
    public function setPackage($pckg)
    {
        $this->pckg = $pckg;
    }

    /**
     * @param string $name the property to store the path in
     */
    public function setName($name)
    {
        $this->name = $name;
    }

}
<?php

/**
 * $Id: aec45bda16dccf81c18561f29f4923bc94fae280 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 *
 * @package phing.tasks.ext
 */

/**
 * Uses the DocBlox_Parallel library to run nested Phing tasks concurrently.
 *
 * WARNING: this task is highly experimental!
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: aec45bda16dccf81c18561f29f4923bc94fae280 $
 * @package phing.tasks.ext
 * @see https://github.com/phpdocumentor/Parallel
 * @since 2.4.10
 */
class ParallelTask extends SequentialTask
{
    /**
     * Maximum number of threads / processes
     * @var int
     */
    private $threadCount = 2;

    /**
     * Sets the maximum number of threads / processes to use
     * @param int $threadCount
     */
    public function setThreadCount($threadCount)
    {
        $this->threadCount = $threadCount;
    }

    public function init()
    {
    }

    public function main()
    {
        @include_once 'phing/contrib/DocBlox/Parallel/Manager.php';
        @include_once 'phing/contrib/DocBlox/Parallel/Worker.php';
        @include_once 'phing/contrib/DocBlox/Parallel/WorkerPipe.php';
        if (!class_exists('DocBlox_Parallel_Worker')) {
            throw new BuildException(
                'ParallelTask depends on DocBlox being installed and on include_path.',
                $this->getLocation()
            );
        }

        $mgr = new DocBlox_Parallel_Manager();
        $mgr->setProcessLimit($this->threadCount);

        foreach ($this->nestedTasks as $task) {
            $worker = new DocBlox_Parallel_Worker(
                array($task, 'perform'),
                array($task)
            );

            $mgr->addWorker($worker);
        }

        $mgr->execute();

        /** @var DocBlox_Parallel_Worker $nestedTask */
        foreach ($mgr as $nestedTask) {
            if ($nestedTask->getError() === "") {
                continue;
            }

            throw new BuildException($nestedTask->getError());
        }
    }
}
<?php
/**
 *  Patches a file by applying a 'diff' file to it
 *
 *  Requires "patch" to be on the execution path.
 *
 *  Based on Apache Ant PatchTask:
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

require_once 'phing/Task.php';

/**
 * Patches a file by applying a 'diff' file to it
 *
 * Requires "patch" to be on the execution path.
 *
 * @package phing.tasks.ext
 */
class PatchTask extends Task
{
    /**
     * Base command to be executed (must end with a space character!)
     * @var string
     */
    const CMD = 'patch --batch --silent ';

    /**
     * File to be patched
     * @var string
     */
    private $originalFile;

    /**
     * Patch file
     *
     * @var string
     */
    private $patchFile;

    /**
     * Value for a "-p" option
     * @var int
     */
    private $strip;

    /**
     * Command line arguments for patch binary
     * @var array
     */
    private $cmdArgs = array();

    /**
     * Halt on error return value from patch invocation.
     * @var bool
     */
    private $haltOnFailure = false;

    /**
     * The file containing the diff output
     *
     * Required.
     *
     * @param  string         $file File containing the diff output
     * @return void
     * @throws BuildException if $file not exists
     */
    public function setPatchFile($file)
    {
        if (!is_file($file)) {
            throw new BuildException(sprintf('Patchfile %s doesn\'t exist', $file));
        }
        $this->patchFile = $file;
    }

    /**
     * The file to patch
     *
     * Optional if it can be inferred from the diff file.
     *
     * @param  string $file File to patch
     * @return void
     */
    public function setOriginalFile($file)
    {
        $this->originalFile = $file;
    }

    /**
     * The name of a file to send the output to, instead of patching
     * the file(s) in place
     *
     * Optional.
     *
     * @param  string $file File to send the output to
     * @return void
     */
    public function setDestFile($file)
    {
        if ($file !== null) {
            $this->cmdArgs [] = "--output=$file";
        }
    }

    /**
     * Flag to create backups
     *
     * Optional, default - false
     *
     * @param  bool $backups If true create backups
     * @return void
     */
    public function setBackups($backups)
    {
        if ($backups) {
            $this->cmdArgs [] = '--backup';
        }
    }

    /**
     * Flag to ignore whitespace differences;
     *
     * Default - false
     *
     * @param  bool $ignore If true ignore whitespace differences
     * @return void
     */
    public function setIgnoreWhiteSpace($ignore)
    {
        if ($ignore) {
            $this->cmdArgs [] = '--ignore-whitespace';
        }
    }

    /**
     * Strip the smallest prefix containing <i>num</i> leading slashes
     * from filenames.
     *
     * patch's <i>--strip</i> option.
     *
     * @param  int            $num number of lines to strip
     * @return void
     * @throws BuildException if num is < 0, or other errors
     */
    public function setStrip($num)
    {
        if ($num < 0) {
            throw new BuildException('strip has to be >= 0');
        }

        $this->strip = $num;
    }

    /**
     * Work silently unless an error occurs
     *
     * Optional, default - false
     * @param  bool $flag If true suppress set the -s option on the patch command
     * @return void
     */
    public function setQuiet($flag)
    {
        if ($flag) {
            $this->cmdArgs [] = '--silent';
        }
    }

    /**
     * Assume patch was created with old and new files swapped
     *
     * Optional, default - false
     *
     * @param  bool $flag If true set the -R option on the patch command
     * @return void
     */
    public function setReverse($flag)
    {
        if ($flag) {
            $this->cmdArgs [] = '--reverse';
        }
    }

    /**
     * The directory to run the patch command in
     *
     * Defaults to the project's base directory.
     *
     * @param  string $directory Directory to run the patch command in
     * @return void
     */
    public function setDir($directory)
    {
        $this->cmdArgs [] = "--directory=$directory";
    }

    /**
     * Ignore patches that seem to be reversed or already applied
     *
     * @param  bool $flag If true set the -N (--forward) option
     * @return void
     */
    public function setForward($flag)
    {
        if ($flag) {
            $this->cmdArgs [] = "--forward";
        }
    }

    /**
     * Set the maximum fuzz factor
     *
     * Defaults to 0
     *
     * @param  string $value Value of a fuzz factor
     * @return void
     */
    public function setFuzz($value)
    {
        $this->cmdArgs [] = "--fuzz=$value";
    }

    /**
     * If true, stop the build process if the patch command
     * exits with an error status.
     *
     * The default is "false"
     *
     * @param  bool $value "true" if it should halt, otherwise "false"
     * @return void
     */
    public function setHaltOnFailure($value)
    {
        $this->haltOnFailure = $value;
    }

    /**
     * Main task method
     *
     * @return void
     * @throws BuildException when it all goes a bit pear shaped
     */
    public function main()
    {
        if ($this->patchFile == null) {
            throw new BuildException('patchfile argument is required');
        }

        // Define patch file
        $this->cmdArgs [] = '-i ' . $this->patchFile;
        // Define strip factor
        if ($this->strip != null) {
            $this->cmdArgs [] = '--strip=' . $this->strip;
        }
        // Define original file if specified
        if ($this->originalFile != null) {
            $this->cmdArgs [] = $this->originalFile;
        }

        $cmd = self::CMD . implode(' ', $this->cmdArgs);

        $this->log('Applying patch: ' . $this->patchFile);

        exec($cmd, $output, $exitCode);

        foreach ($output as $line) {
            $this->log($line, Project::MSG_VERBOSE);
        }

        if ($exitCode != 0 && $this->haltOnFailure) {
            throw new BuildException("Task exited with code $exitCode");
        }

    }
}
<?php
/**
 * $Id: e97cb62953c381f62ab548dfe073b26071f0eb0a $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/PhingFile.php';

/**
 * Analyzer element for the PhpDependTask
 *
 * @package phing.tasks.ext.pdepend
 * @author  Benjamin Schultz <bschultz@proqrent.de>
 * @version $Id: e97cb62953c381f62ab548dfe073b26071f0eb0a $
 * @since   2.4.1
 */
class PhpDependAnalyzerElement
{
    /**
     * The type of the analyzer
     *
     * @var string
     */
    protected $type = '';

    /**
     * The value(s) for the analyzer option
     *
     * @var array
     */
    protected $value = array();

    /**
     * Sets the analyzer type
     *
     * @param string $type Type of the analyzer
     *
     * @throws BuildException
     */
    public function setType($type)
    {
        $this->type = $type;

        switch ($this->type) {
            case 'coderank-mode':
                break;

            default:
                throw new BuildException('Analyzer "' . $this->type . '" not implemented');
        }
    }

    /**
     * Get the analyzer type
     *
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Sets the value for the analyzer
     *
     * @param string $value Value for the analyzer
     */
    public function setValue($value)
    {
        $this->value = array();

        $token = ' ,;';
        $values = strtok($value, $token);

        while ($values !== false) {
            $this->value[] = $values;
            $values = strtok($token);
        }
    }

    /**
     * Get the analyzer value
     *
     * @return string
     */
    public function getValue()
    {
        return $this->value;
    }
}
<?php
/**
 * $Id: c5b569118db6a6d66c6568702f14e8ae37fad9a3 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/PhingFile.php';

/**
 * Logger element for the PhpDependTask.
 *
 * @package phing.tasks.ext.pdepend
 * @author  Benjamin Schultz <bschultz@proqrent.de>
 * @version $Id: c5b569118db6a6d66c6568702f14e8ae37fad9a3 $
 * @since   2.4.1
 */
class PhpDependLoggerElement
{
    /**
     * The type of the logger.
     *
     * @var string
     */
    protected $type = '';

    /**
     * Output file for logger.
     *
     * @var PhingFile
     */
    protected $outfile = null;

    /**
     * Sets the logger type.
     *
     * @param string $type Type of the logger
     *
     * @throws BuildException
     */
    public function setType($type)
    {
        $this->type = $type;

        switch ($this->type) {
            case 'jdepend-chart':
            case 'jdepend-xml':
            case 'overview-pyramid':
            case 'phpunit-xml':
            case 'summary-xml':
                break;

            default:
                throw new BuildException('Logger "' . $this->type . '" not implemented');
        }
    }

    /**
     * Get the logger type
     *
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Sets the output file for the logger results.
     *
     * @param PhingFile $outfile The output file
     */
    public function setOutfile(PhingFile $outfile)
    {
        $this->outfile = $outfile;
    }

    /**
     * Get the output file.
     *
     * @return PhingFile
     */
    public function getOutfile()
    {
        return $this->outfile;
    }
}
<?php
/**
 *  $Id: 7ef44b418426e61cb1ea3b8cf87b289d79f663aa $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/pdepend/PhpDependLoggerElement.php';
require_once 'phing/tasks/ext/pdepend/PhpDependAnalyzerElement.php';

/**
 * Runs the PHP_Depend software analyzer and metric tool.
 * Performs static code analysis on a given source base.
 *
 * @package phing.tasks.ext.pdepend
 * @author  Benjamin Schultz <bschultz@proqrent.de>
 * @version $Id: 7ef44b418426e61cb1ea3b8cf87b289d79f663aa $
 * @since   2.4.1
 */
class PhpDependTask extends Task
{
    /**
     * A php source code filename or directory
     *
     * @var PhingFile
     */
    protected $file = null;

    /**
     * All fileset objects assigned to this task
     *
     * @var FileSet[]
     */
    protected $filesets = array();

    /**
     * List of allowed file extensions. Default file extensions are <b>php</b>
     * and <p>php5</b>.
     *
     * @var array<string>
     */
    protected $allowedFileExtensions = array('php', 'php5');

    /**
     * List of exclude directories. Default exclude dirs are <b>.git</b>,
     * <b>.svn</b> and <b>CVS</b>.
     *
     * @var array<string>
     */
    protected $excludeDirectories = array('.git', '.svn', 'CVS');

    /**
     * List of exclude packages
     *
     * @var array<string>
     */
    protected $excludePackages = array();

    /**
     * Should the parse ignore doc comment annotations?
     *
     * @var boolean
     */
    protected $withoutAnnotations = false;

    /**
     * Should PHP_Depend treat <b>+global</b> as a regular project package?
     *
     * @var boolean
     */
    protected $supportBadDocumentation = false;

    /**
     * Flag for enable/disable debugging
     *
     * @var boolean
     */
    protected $debug = false;

    /**
     * PHP_Depend configuration file
     *
     * @var PhingFile
     */
    protected $configFile = null;

    /**
     * Logger elements
     *
     * @var PhpDependLoggerElement[]
     */
    protected $loggers = array();

    /**
     * Analyzer elements
     *
     * @var PhpDependAnalyzerElement[]
     */
    protected $analyzers = array();

    /**
     * Holds the PHP_Depend runner instance
     *
     * @var PHP_Depend_TextUI_Runner
     */
    protected $runner = null;

    /**
     * Flag that determines whether to halt on error
     *
     * @var boolean
     */
    protected $haltonerror = false;

    /**
     * @var bool
     */
    private $oldVersion = false;

    /**
     * @var string
     */
    protected $pharLocation = "";

    /**
     * Load the necessary environment for running PHP_Depend
     *
     * @throws BuildException
     */
    protected function requireDependencies()
    {
        if (!empty($this->pharLocation)) {
            include_once 'phar://' . $this->pharLocation . '/vendor/autoload.php';
        }

        // check 2.x version (composer/phar)
        if (class_exists('PDepend\\TextUI\\Runner')) {
            return;
        }

        $this->oldVersion = true;

        // check 1.x version (composer)
        if (class_exists('PHP_Depend_TextUI_Runner')) {
            // include_path hack for PHP_Depend 1.1.3
            $rc = new ReflectionClass('PHP_Depend');
            set_include_path(get_include_path() . ":" . realpath(dirname($rc->getFileName()) . "/../"));

            return;
        }

        @include_once 'PHP/Depend/Autoload.php';

        if (!class_exists('PHP_Depend_Autoload')) {
            throw new BuildException(
                'PhpDependTask depends on PHP_Depend being installed and on include_path',
                $this->getLocation()
            );
        }

        // register PHP_Depend autoloader
        $autoload = new PHP_Depend_Autoload();
        $autoload->register();
    }

    /**
     * Set the input source file or directory
     *
     * @param PhingFile $file The input source file or directory
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Sets a list of filename extensions for valid php source code files
     *
     * @param string $fileExtensions List of valid file extensions
     */
    public function setAllowedFileExtensions($fileExtensions)
    {
        $this->allowedFileExtensions = array();

        $token = ' ,;';
        $ext = strtok($fileExtensions, $token);

        while ($ext !== false) {
            $this->allowedFileExtensions[] = $ext;
            $ext = strtok($token);
        }
    }

    /**
     * Sets a list of exclude directories
     *
     * @param string $excludeDirectories List of exclude directories
     */
    public function setExcludeDirectories($excludeDirectories)
    {
        $this->excludeDirectories = array();

        $token = ' ,;';
        $pattern = strtok($excludeDirectories, $token);

        while ($pattern !== false) {
            $this->excludeDirectories[] = $pattern;
            $pattern = strtok($token);
        }
    }

    /**
     * Sets a list of exclude packages
     *
     * @param string $excludePackages Exclude packages
     */
    public function setExcludePackages($excludePackages)
    {
        $this->excludePackages = array();

        $token = ' ,;';
        $pattern = strtok($excludePackages, $token);

        while ($pattern !== false) {
            $this->excludePackages[] = $pattern;
            $pattern = strtok($token);
        }
    }

    /**
     * Should the parser ignore doc comment annotations?
     *
     * @param boolean $withoutAnnotations
     */
    public function setWithoutAnnotations($withoutAnnotations)
    {
        $this->withoutAnnotations = StringHelper::booleanValue($withoutAnnotations);
    }

    /**
     * Should PHP_Depend support projects with a bad documentation. If this
     * option is set to <b>true</b>, PHP_Depend will treat the default package
     * <b>+global</b> as a regular project package.
     *
     * @param boolean $supportBadDocumentation
     */
    public function setSupportBadDocumentation($supportBadDocumentation)
    {
        $this->supportBadDocumentation = StringHelper::booleanValue($supportBadDocumentation);
    }

    /**
     * Set debugging On/Off
     *
     * @param boolean $debug
     */
    public function setDebug($debug)
    {
        $this->debug = StringHelper::booleanValue($debug);
    }

    /**
     * Set halt on error
     *
     * @param boolean $haltonerror
     */
    public function setHaltonerror($haltonerror)
    {
        $this->haltonerror = StringHelper::booleanValue($haltonerror);
    }

    /**
     * Set the configuration file
     *
     * @param PhingFile $configFile The configuration file
     */
    public function setConfigFile(PhingFile $configFile)
    {
        $this->configFile = $configFile;
    }

    /**
     * Create object for nested logger element
     *
     * @return PhpDependLoggerElement
     */
    public function createLogger()
    {
        $num = array_push($this->loggers, new PhpDependLoggerElement());

        return $this->loggers[$num - 1];
    }

    /**
     * Create object for nested analyzer element
     *
     * @return PhpDependAnalyzerElement
     */
    public function createAnalyzer()
    {
        $num = array_push($this->analyzers, new PhpDependAnalyzerElement());

        return $this->analyzers[$num - 1];
    }

    /**
     * @param string $pharLocation
     */
    public function setPharLocation($pharLocation)
    {
        $this->pharLocation = $pharLocation;
    }

    /**
     * Executes PHP_Depend_TextUI_Runner against PhingFile or a FileSet
     *
     * @throws BuildException
     */
    public function main()
    {
        $this->requireDependencies();

        if (!isset($this->file) and count($this->filesets) == 0) {
            throw new BuildException('Missing either a nested fileset or attribute "file" set');
        }

        if (count($this->loggers) == 0) {
            throw new BuildException('Missing nested "logger" element');
        }

        $this->validateLoggers();
        $this->validateAnalyzers();

        $filesToParse = $this->getFilesToParse();

        $runner = $this->createRunner();
        $runner->setSourceArguments($filesToParse);

        foreach ($this->loggers as $logger) {
            // Register logger
            if ($this->oldVersion) {
                $runner->addLogger(
                    $logger->getType(),
                    $logger->getOutfile()->__toString()
                );
            } else {
                $runner->addReportGenerator(
                    $logger->getType(),
                    $logger->getOutfile()->__toString()
                );
            }
        }

        foreach ($this->analyzers as $analyzer) {
            // Register additional analyzer
            $runner->addOption(
                $analyzer->getType(),
                $analyzer->getValue()
            );
        }

        // Disable annotation parsing
        if ($this->withoutAnnotations) {
            $runner->setWithoutAnnotations();
        }

        // Enable bad documentation support
        if ($this->supportBadDocumentation) {
            $runner->setSupportBadDocumentation();
        }

        // Check for suffix
        if (count($this->allowedFileExtensions) > 0) {
            $runner->setFileExtensions($this->allowedFileExtensions);
        }

        // Check for ignore directories
        if (count($this->excludeDirectories) > 0) {
            $runner->setExcludeDirectories($this->excludeDirectories);
        }

        // Check for exclude packages
        if (count($this->excludePackages) > 0) {
            $runner->setExcludePackages($this->excludePackages);
        }

        $runner->run();

        if ($runner->hasParseErrors() === true) {
            $this->log('Following errors occurred:');

            foreach ($runner->getParseErrors() as $error) {
                $this->log($error);
            }

            if ($this->haltonerror === true) {
                throw new BuildException('Errors occurred during parse process');
            }
        }
    }

    /**
     * Validates the available loggers
     *
     * @throws BuildException
     */
    protected function validateLoggers()
    {
        foreach ($this->loggers as $logger) {
            if ($logger->getType() === '') {
                throw new BuildException('Logger missing required "type" attribute');
            }

            if ($logger->getOutfile() === null) {
                throw new BuildException('Logger requires "outfile" attribute');
            }
        }
    }

    /**
     * Validates the available analyzers
     *
     * @throws BuildException
     */
    protected function validateAnalyzers()
    {
        foreach ($this->analyzers as $analyzer) {
            if ($analyzer->getType() === '') {
                throw new BuildException('Analyzer missing required "type" attribute');
            }

            if (count($analyzer->getValue()) === 0) {
                throw new BuildException('Analyzer missing required "value" attribute');
            }
        }
    }

    /**
     * @return array
     */
    private function getFilesToParse()
    {
        $filesToParse = array();

        if ($this->file instanceof PhingFile) {
            $filesToParse[] = $this->file->__toString();
            return $filesToParse;
        } else {
            // append any files in filesets
            foreach ($this->filesets as $fs) {
                $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles();

                foreach ($files as $filename) {
                    $f = new PhingFile($fs->getDir($this->project), $filename);
                    $filesToParse[] = $f->getAbsolutePath();
                }
            }
            return $filesToParse;
        }
    }

    /**
     * @return object
     */
    private function createRunner()
    {
        if ($this->oldVersion) {
            return $this->createLegacyRunner();
        }

        $applicationClassName = 'PDepend\\Application';
        $application = new $applicationClassName();

        $runner = $application->getRunner();

        $configuration = $this->getConfiguration();

        if ($configuration === null) {
            $configuration = $application->getConfiguration();
        }

        if ($this->debug) {
            // Enable debug logging
            call_user_func('PDepend\\Util\\Log::setSeverity', 1);
        }

        call_user_func('PDepend\\Util\\ConfigurationInstance::set', $configuration);

        return $runner;
    }

    /**
     * @return PHP_Depend_TextUI_Runner
     */
    private function createLegacyRunner()
    {
        $runner = new PHP_Depend_TextUI_Runner();
        $runner->addProcessListener(new PHP_Depend_TextUI_ResultPrinter());

        if ($this->debug) {
            require_once 'PHP/Depend/Util/Log.php';
            // Enable debug logging
            PHP_Depend_Util_Log::setSeverity(PHP_Depend_Util_Log::DEBUG);
        }

        $configuration = $this->getConfiguration();

        if ($configuration === null) {
            $configurationFactory = new PHP_Depend_Util_Configuration_Factory();
            $configuration = $configurationFactory->createDefault();
        }

        PHP_Depend_Util_ConfigurationInstance::set($configuration);
        $runner->setConfiguration($configuration);

        return $runner;
    }

    /**
     * Loads configuration file
     * @return null|PHP_Depend_Util_Configuration
     * @throws BuildException
     */
    private function getConfiguration()
    {
        // Check for configuration option
        if ($this->configFile == null || ! ($this->configFile instanceof PhingFile)) {
            return null;
        }

        if (file_exists($this->configFile->__toString()) === false) {
            throw new BuildException(
                'The configuration file "' . $this->configFile->__toString() . '" doesn\'t exist.'
            );
        }

        if ($this->oldVersion) {
            $configurationClassName = 'PHP_Depend_Util_Configuration';
        } else {
            $configurationClassName = 'PDepend\\Util\\Configuration';
        }

        return new $configurationClassName(
            $this->configFile->__toString(),
            null,
            true
        );
    }
}
<?php

/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 *
 * @version SVN: $Id: 43afd902ddb58980b64121603ad61d308ca88975 $
 * @package phing.tasks.ext.pdo
 */

require_once 'phing/tasks/ext/pdo/PDOQuerySplitter.php';

/**
 * Splits SQL source into queries using simple regular expressions
 *
 * Extracted from PDOSQLExecTask::runStatements()
 *
 * @author  Hans Lellelid <hans@xmpl.org>
 * @author  Alexey Borzov <avb@php.net>
 * @package phing.tasks.ext.pdo
 * @version $Id: 43afd902ddb58980b64121603ad61d308ca88975 $
 */
class DefaultPDOQuerySplitter extends PDOQuerySplitter
{
    /**
     * Delimiter type, one of PDOSQLExecTask::DELIM_ROW or PDOSQLExecTask::DELIM_NORMAL
     * @var string
     */
    private $delimiterType;

    /**
     * Leftover SQL from previous line
     * @var string
     */
    private $sqlBacklog = '';

    /**
     * Constructor, sets the parent task, reader with SQL source and delimiter type
     *
     * @param PDOSQLExecTask $parent
     * @param Reader         $reader
     * @param string         $delimiterType
     */
    public function __construct(PDOSQLExecTask $parent, Reader $reader, $delimiterType = PDOSQLExecTask::DELIM_NORMAL)
    {
        parent::__construct($parent, $reader);
        $this->delimiterType = $delimiterType;
    }

    /**
     * Returns next query from SQL source, null if no more queries left
     *
     * In case of "row" delimiter type this searches for strings containing only
     * delimiters. In case of "normal" delimiter type, this uses simple regular
     * expression logic to search for delimiters.
     *
     * @return string|null
     */
    public function nextQuery()
    {
        $sql = "";
        $hasQuery = false;

        while (($line = $this->sqlReader->readLine()) !== null) {
            $delimiter = $this->parent->getDelimiter();
            $project = $this->parent->getOwningTarget()->getProject();
            $line = ProjectConfigurator::replaceProperties(
                $project,
                trim($line),
                $project->getProperties()
            );

            if (($line != $delimiter) && (
                    StringHelper::startsWith("//", $line) ||
                    StringHelper::startsWith("--", $line) ||
                    StringHelper::startsWith("#", $line))
            ) {
                continue;
            }

            if (strlen($line) > 4
                && strtoupper(substr($line, 0, 4)) == "REM "
            ) {
                continue;
            }

            // MySQL supports defining new delimiters
            if (preg_match('/DELIMITER [\'"]?([^\'" $]+)[\'"]?/i', $line, $matches)) {
                $this->parent->setDelimiter($matches[1]);
                continue;
            }

            if ($this->sqlBacklog !== "") {
                $sql = $this->sqlBacklog;
                $this->sqlBacklog = "";
            }

            $sql .= " " . $line . "\n";

            // SQL defines "--" as a comment to EOL
            // and in Oracle it may contain a hint
            // so we cannot just remove it, instead we must end it
            if (strpos($line, "--") !== false) {
                $sql .= "\n";
            }

            // DELIM_ROW doesn't need this (as far as i can tell)
            if ($this->delimiterType == PDOSQLExecTask::DELIM_NORMAL) {

                $reg = "#((?:\"(?:\\\\.|[^\"])*\"?)+|'(?:\\\\.|[^'])*'?|" . preg_quote($delimiter) . ")#";

                $sqlParts = preg_split($reg, $sql, 0, PREG_SPLIT_DELIM_CAPTURE);
                $this->sqlBacklog = "";
                foreach ($sqlParts as $sqlPart) {
                    // we always want to append, even if it's a delim (which will be stripped off later)
                    $this->sqlBacklog .= $sqlPart;

                    // we found a single (not enclosed by ' or ") delimiter, so we can use all stuff before the delim as the actual query
                    if ($sqlPart === $delimiter) {
                        $sql = $this->sqlBacklog;
                        $this->sqlBacklog = "";
                        $hasQuery = true;
                    }
                }
            }

            if ($hasQuery || ($this->delimiterType == PDOSQLExecTask::DELIM_ROW && $line == $delimiter)) {
                // this assumes there is always a delimter on the end of the SQL statement.
                $sql = StringHelper::substring(
                    $sql,
                    0,
                    strlen($sql) - strlen($delimiter)
                    - ($this->delimiterType == PDOSQLExecTask::DELIM_ROW ? 2 : 1)
                );

                return $sql;
            }
        }

        // Catch any statements not followed by ;
        if ($sql !== "") {
            return $sql;
        }

        return null;
    }
}
<?php

/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 *
 * @version $Id: 9e64e7e9e66982fa11e85ccde7b9ab5756076872 $
 * @package phing.tasks.ext.pdo
 */

require_once 'phing/tasks/ext/pdo/PDOQuerySplitter.php';

/**
 * Dummy query splitter: converts entire input into single
 * SQL string
 *
 * @author  Michiel Rook <mrook@php.net>
 * @package phing.tasks.ext.pdo
 * @version $Id: 9e64e7e9e66982fa11e85ccde7b9ab5756076872 $
 */
class DummyPDOQuerySplitter extends PDOQuerySplitter
{
    /**
     * Returns entire SQL source
     *
     * @return string|null
     */
    public function nextQuery()
    {
        $sql = null;

        while (($line = $this->sqlReader->readLine()) !== null) {
            $delimiter = $this->parent->getDelimiter();
            $project = $this->parent->getOwningTarget()->getProject();
            $line = ProjectConfigurator::replaceProperties(
                $project,
                trim($line),
                $project->getProperties()
            );

            if (($line != $delimiter) && (
                    StringHelper::startsWith("//", $line) ||
                    StringHelper::startsWith("--", $line) ||
                    StringHelper::startsWith("#", $line))
            ) {
                continue;
            }

            $sql .= " " . $line . "\n";

            /**
             * fix issue with PDO and wrong formated multistatements
             * @issue 1108
             */
            if (StringHelper::endsWith($delimiter, $line)) {
                break;
            }

        }

        return $sql;
    }
}
<?php

/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 *
 * @version $Id: 918ec6d58131d6aea966b154be71a83c27e94b7c $
 * @package phing.tasks.ext.pdo
 */

/**
 * Base class for classes that split SQL source into separate queries
 *
 * @author  Alexey Borzov <avb@php.net>
 * @package phing.tasks.ext.pdo
 * @version $Id: 918ec6d58131d6aea966b154be71a83c27e94b7c $
 */
abstract class PDOQuerySplitter
{
    /**
     * Task that uses the splitter
     * @var PDOSQLExecTask
     */
    protected $parent;

    /**
     * Reader with SQL source
     * @var BufferedReader
     */
    protected $sqlReader;

    /**
     * Constructor, sets the parent task and reader with SQL source
     *
     * @param PDOSQLExecTask $parent
     * @param Reader         $reader
     */
    public function __construct(PDOSQLExecTask $parent, Reader $reader)
    {
        $this->parent = $parent;
        $this->sqlReader = new BufferedReader($reader);
    }

    /**
     * Returns next query from SQL source, null if no more queries left
     *
     * @return string|null
     */
    abstract public function nextQuery();
}
<?php
/**
 * $Id: 07ef257c685f448c71fffb98303cbedd07a92245 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/PhingFile.php';

/**
 * Abstract
 *
 * @author Hans Lellelid <hans@xmpl.org>
 * @package phing.tasks.ext.pdo
 * @since 2.3.0
 */
abstract class PDOResultFormatter
{
    /**
     * Output writer.
     *
     * @var Writer
     */
    protected $out;

    /**
     * Sets the output writer.
     *
     * @param Writer $out
     */
    public function setOutput(Writer $out)
    {
        $this->out = $out;
    }

    /**
     * Gets the output writer.
     *
     * @return Writer
     */
    public function getOutput()
    {
        return $this->out;
    }

    /**
     * Gets the preferred output filename for this formatter.
     * @return string
     */
    abstract public function getPreferredOutfile();

    /**
     * Perform any initialization.
     */
    public function initialize()
    {

    }

    /**
     * Processes a specific row from PDO result set.
     *
     * @param array $row Row of PDO result set.
     */
    abstract public function processRow($row);

    /**
     * Perform any final tasks and Close the writer.
     */
    public function close()
    {
        $this->out->close();
    }
}
<?php
/**
 * $Id: f06dbbcba80e0b0ea474734e7e0dbc439d957479 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/PhingFile.php';
require_once 'phing/tasks/ext/pdo/PlainPDOResultFormatter.php';
require_once 'phing/tasks/ext/pdo/XMLPDOResultFormatter.php';
require_once 'phing/util/LogWriter.php';

/**
 * A class to represent the nested <formatter> element for PDO SQL results.
 *
 * This class is inspired by the similarly-named class in the PHPUnit tasks.
 *
 * @author Hans Lellelid <hans@xmpl.org>
 * @package phing.tasks.ext.pdo
 * @since 2.3.0
 */
class PDOSQLExecFormatterElement
{
    /**
     * @var PDOResultFormatter
     */
    private $formatter;

    /**
     * The type of the formatter (used for built-in formatter classes).
     * @var string
     */
    private $type = "";

    /**
     * Whether to use file (or write output to phing log).
     * @var boolean
     */
    private $useFile = true;

    /**
     * Output file for formatter.
     * @var PhingFile
     */
    private $outfile;

    /**
     * Print header columns.
     * @var boolean
     */
    private $showheaders = true;

    /**
     * Whether to format XML output.
     * @var boolean
     */
    private $formatoutput = true;

    /**
     * Encoding for XML output.
     * @var string
     */
    private $encoding;

    /**
     * Column delimiter.
     * Defaults to ','
     * @var string
     */
    private $coldelimiter = ",";

    /**
     * Row delimiter.
     * Defaults to PHP_EOL.
     * @var string
     */
    private $rowdelimiter = PHP_EOL;

    /**
     * Append to an existing file or overwrite it?
     * @var boolean
     */
    private $append = false;

    /**
     * Parameters for a custom formatter.
     * @var array Parameter[]
     */
    private $formatterParams = array();

    /**
     * @var PDOSQLExecTask
     */
    private $parentTask;

    /**
     * Construct a new PDOSQLExecFormatterElement with parent task.
     * @param PDOSQLExecTask $parentTask
     */
    public function __construct(PDOSQLExecTask $parentTask)
    {
        $this->parentTask = $parentTask;
    }

    /**
     * Supports nested <param> element (for custom formatter classes).
     * @return Parameter
     */
    public function createParam()
    {
        $num = array_push($this->parameters, new Parameter());

        return $this->parameters[$num - 1];
    }

    /**
     * Gets a configured output writer.
     * @return Writer
     */
    private function getOutputWriter()
    {
        if ($this->useFile) {
            $of = $this->getOutfile();
            if (!$of) {
                $of = new PhingFile($this->formatter->getPreferredOutfile());
            }

            return new FileWriter($of, $this->append);
        } else {
            return $this->getDefaultOutput();
        }
    }

    /**
     * Configures wrapped formatter class with any attributes on this element.
     */
    public function prepare()
    {

        if (!$this->formatter) {
            throw new BuildException("No formatter specified (use type or classname attribute)", $this->getLocation());
        }

        $out = $this->getOutputWriter();

        $this->parentTask->log("Setting output writer to: " . get_class($out), Project::MSG_VERBOSE);
        $this->formatter->setOutput($out);

        if ($this->formatter instanceof PlainPDOResultFormatter) {
            // set any options that apply to the plain formatter
            $this->formatter->setShowheaders($this->showheaders);
            $this->formatter->setRowdelim($this->rowdelimiter);
            $this->formatter->setColdelim($this->coldelimiter);
        } elseif ($this->formatter instanceof XMLPDOResultFormatter) {
            // set any options that apply to the xml formatter
            $this->formatter->setEncoding($this->encoding);
            $this->formatter->setFormatOutput($this->formatoutput);
        }

        foreach ($this->formatterParams as $param) {
            $param = new Parameter();
            $method = 'set' . $param->getName();
            if (!method_exists($this->formatter, $param->getName())) {
                throw new BuildException("Formatter " . get_class(
                        $this->formatter
                    ) . " does not have a $method method.", $this->getLocation());
            }
            call_user_func(array($this->formatter, $method), $param->getValue());
        }
    }

    /**
     * Sets the formatter type.
     * @param string $type
     * @throws BuildException
     */
    public function setType($type)
    {
        $this->type = $type;
        if ($this->type == "xml") {
            $this->formatter = new XMLPDOResultFormatter();
        } elseif ($this->type == "plain") {
            $this->formatter = new PlainPDOResultFormatter();
        } else {
            throw new BuildException("Formatter '" . $this->type . "' not implemented");
        }
    }

    /**
     * Set classname for a custom formatter (must extend PDOResultFormatter).
     * @param string $className
     */
    public function setClassName($className)
    {
        $classNameNoDot = Phing::import($className);
        $this->formatter = new $classNameNoDot();
    }

    /**
     * Set whether to write formatter results to file.
     * @param boolean $useFile
     */
    public function setUseFile($useFile)
    {
        $this->useFile = (boolean) $useFile;
    }

    /**
     * Return whether to write formatter results to file.
     * @return boolean
     */
    public function getUseFile()
    {
        return $this->useFile;
    }

    /**
     * Sets the output file for the formatter results.
     * @param PhingFile $outfile
     * @internal param PhingFile $outFile
     */
    public function setOutfile(PhingFile $outfile)
    {
        $this->outfile = $outfile;
    }

    /**
     * Get the output file.
     * @return PhingFile
     */
    public function getOutfile()
    {
        return $this->outfile;
        /*
        } else {
            return new PhingFile($this->formatter->getPreferredOutfile());
        }*/
    }

    /**
     * whether output should be appended to or overwrite
     * an existing file.  Defaults to false.
     * @param boolean $append
     */
    public function setAppend($append)
    {
        $this->append = (boolean) $append;
    }

    /**
     * Whether output should be appended to file.
     * @return boolean
     */
    public function getAppend()
    {
        return $this->append;
    }

    /**
     * Print headers for result sets from the
     * statements; optional, default true.
     * @param boolean $showheaders
     */
    public function setShowheaders($showheaders)
    {
        $this->showheaders = (boolean) $showheaders;
    }

    /**
     * Sets the column delimiter.
     * @param string $v
     */
    public function setColdelim($v)
    {
        $this->coldelimiter = $v;
    }

    /**
     * Sets the row delimiter.
     * @param string $v
     */
    public function setRowdelim($v)
    {
        $this->rowdelimiter = $v;
    }

    /**
     * Set the DOM document encoding.
     * @param string $v
     */
    public function setEncoding($v)
    {
        $this->encoding = $v;
    }

    /**
     * @param boolean $v
     */
    public function setFormatOutput($v)
    {
        $this->formatOutput = (boolean) $v;
    }

    /**
     * Gets a default output writer for this task.
     * @return Writer
     */
    private function getDefaultOutput()
    {
        return new LogWriter($this->parentTask);
    }

    /**
     * Gets the formatter that has been configured based on this element.
     * @return PDOResultFormatter
     */
    public function getFormatter()
    {
        return $this->formatter;
    }
}
<?php
/*
 *  $Id: 6f0271c4fe34f85450002311f9fdb18b359bea8d $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/pdo/PDOTask.php';
include_once 'phing/system/io/StringReader.php';
include_once 'phing/tasks/ext/pdo/PDOSQLExecFormatterElement.php';

/**
 * Executes a series of SQL statements on a database using PDO.
 *
 * <p>Statements can
 * either be read in from a text file using the <i>src</i> attribute or from
 * between the enclosing SQL tags.</p>
 *
 * <p>Multiple statements can be provided, separated by semicolons (or the
 * defined <i>delimiter</i>). Individual lines within the statements can be
 * commented using either --, // or REM at the start of the line.</p>
 *
 * <p>The <i>autocommit</i> attribute specifies whether auto-commit should be
 * turned on or off whilst executing the statements. If auto-commit is turned
 * on each statement will be executed and committed. If it is turned off the
 * statements will all be executed as one transaction.</p>
 *
 * <p>The <i>onerror</i> attribute specifies how to proceed when an error occurs
 * during the execution of one of the statements.
 * The possible values are: <b>continue</b> execution, only show the error;
 * <b>stop</b> execution and commit transaction;
 * and <b>abort</b> execution and transaction and fail task.</p>
 *
 * @author    Hans Lellelid <hans@xmpl.org> (Phing)
 * @author    Jeff Martin <jeff@custommonkey.org> (Ant)
 * @author    Michael McCallum <gholam@xtra.co.nz> (Ant)
 * @author    Tim Stephenson <tim.stephenson@sybase.com> (Ant)
 * @package   phing.tasks.ext.pdo
 * @version   $Id: 6f0271c4fe34f85450002311f9fdb18b359bea8d $
 */
class PDOSQLExecTask extends PDOTask
{

    /**
     * Count of how many statements were executed successfully.
     * @var int
     */
    private $goodSql = 0;

    /**
     * Count of total number of SQL statements.
     * @var int
     */
    private $totalSql = 0;

    const DELIM_ROW = "row";
    const DELIM_NORMAL = "normal";
    const DELIM_NONE = "none";

    /**
     * Database connection
     * @var PDO
     */
    private $conn = null;

    /**
     * Files to load
     * @var array FileSet[]
     */
    private $filesets = array();

    /**
     * Files to load
     * @var array FileList[]
     */
    private $filelists = array();

    /**
     * Formatter elements.
     * @var array PDOSQLExecFormatterElement[]
     */
    private $formatters = array();

    /**
     * SQL statement
     * @var PDOStatement
     */
    private $statement;

    /**
     * SQL input file
     * @var PhingFile
     */
    private $srcFile;

    /**
     * SQL input command
     * @var string
     */
    private $sqlCommand = "";

    /**
     * SQL transactions to perform
     */
    private $transactions = array();

    /**
     * SQL Statement delimiter (for parsing files)
     * @var string
     */
    private $delimiter = ";";

    /**
     * The delimiter type indicating whether the delimiter will
     * only be recognized on a line by itself
     */
    private $delimiterType = "none"; // can't use constant just defined

    /**
     * Action to perform if an error is found
     **/
    private $onError = "abort";

    /**
     * Encoding to use when reading SQL statements from a file
     */
    private $encoding = null;

    /**
     * Fetch mode for PDO select queries.
     * @var int
     */
    private $fetchMode;

    /**
     * Set the name of the SQL file to be run.
     * Required unless statements are enclosed in the build file
     * @param PhingFile $srcFile
     */
    public function setSrc(PhingFile $srcFile)
    {
        $this->srcFile = $srcFile;
    }

    /**
     * Set an inline SQL command to execute.
     * NB: Properties are not expanded in this text.
     * @param $sql
     */
    public function addText($sql)
    {
        $this->sqlCommand .= $sql;
    }

    /**
     * Adds a set of files (nested fileset attribute).
     * @param FileSet $set
     */
    public function addFileset(FileSet $set)
    {
        $this->filesets[] = $set;
    }

    /**
     * Adds a set of files (nested filelist attribute).
     * @param FileList $list
     */
    public function addFilelist(FileList $list)
    {
        $this->filelists[] = $list;
    }

    /**
     * Creates a new PDOSQLExecFormatterElement for <formatter> element.
     * @return PDOSQLExecFormatterElement
     */
    public function createFormatter()
    {
        $fe = new PDOSQLExecFormatterElement($this);
        $this->formatters[] = $fe;

        return $fe;
    }

    /**
     * Add a SQL transaction to execute
     */
    public function createTransaction()
    {
        $t = new PDOSQLExecTransaction($this);
        $this->transactions[] = $t;

        return $t;
    }

    /**
     * Set the file encoding to use on the SQL files read in
     *
     * @param the $encoding
     * @internal param the $encoding encoding to use on the files
     */
    public function setEncoding($encoding)
    {
        $this->encoding = $encoding;
    }

    /**
     * Set the statement delimiter.
     *
     * <p>For example, set this to "go" and delimitertype to "ROW" for
     * Sybase ASE or MS SQL Server.</p>
     *
     * @param delimiter
     */
    public function setDelimiter($delimiter)
    {
        $this->delimiter = $delimiter;
    }

    /**
     * Get the statement delimiter.
     *
     * @return string
     */
    public function getDelimiter()
    {
        return $this->delimiter;
    }

    /**
     * Set the Delimiter type for this sql task. The delimiter type takes two
     * values - normal and row. Normal means that any occurrence of the delimiter
     * terminate the SQL command whereas with row, only a line containing just
     * the delimiter is recognized as the end of the command.
     *
     * @param string $delimiterType
     */
    public function setDelimiterType($delimiterType)
    {
        $this->delimiterType = $delimiterType;
    }

    /**
     * Action to perform when statement fails: continue, stop, or abort
     * optional; default &quot;abort&quot;
     * @param $action
     */
    public function setOnerror($action)
    {
        $this->onError = $action;
    }

    /**
     * Sets the fetch mode to use for the PDO resultset.
     * @param mixed $mode The PDO fetchmode integer or constant name.
     * @throws BuildException
     */
    public function setFetchmode($mode)
    {
        if (is_numeric($mode)) {
            $this->fetchMode = (int) $mode;
        } else {
            if (defined($mode)) {
                $this->fetchMode = constant($mode);
            } else {
                throw new BuildException("Invalid PDO fetch mode specified: " . $mode, $this->getLocation());
            }
        }
    }

    /**
     * Gets a default output writer for this task.
     *
     * @return Writer
     */
    private function getDefaultOutput()
    {
        return new LogWriter($this);
    }

    /**
     * Load the sql file and then execute it.
     *
     * {@inheritdoc}
     *
     * @throws BuildException
     */
    public function main()
    {

        // Set a default fetchmode if none was specified
        // (We're doing that here to prevent errors loading the class is PDO is not available.)
        if ($this->fetchMode === null) {
            $this->fetchMode = PDO::FETCH_ASSOC;
        }

        // Initialize the formatters here.  This ensures that any parameters passed to the formatter
        // element get passed along to the actual formatter object
        foreach ($this->formatters as $fe) {
            $fe->prepare();
        }

        $savedTransaction = array();
        for ($i = 0, $size = count($this->transactions); $i < $size; $i++) {
            $savedTransaction[] = clone $this->transactions[$i];
        }

        $savedSqlCommand = $this->sqlCommand;

        $this->sqlCommand = trim($this->sqlCommand);

        try {
            if ($this->srcFile === null && $this->sqlCommand === ""
                && empty($this->filesets) && empty($this->filelists)
                && count($this->transactions) === 0
            ) {
                throw new BuildException("Source file or fileset/filelist, "
                    . "transactions or sql statement "
                    . "must be set!", $this->location);
            }

            if ($this->srcFile !== null && !$this->srcFile->exists()) {
                throw new BuildException("Source file does not exist!", $this->location);
            }

            // deal with the filesets
            foreach ($this->filesets as $fs) {
                $ds = $fs->getDirectoryScanner($this->project);
                $srcDir = $fs->getDir($this->project);
                $srcFiles = $ds->getIncludedFiles();
                // Make a transaction for each file
                foreach ($srcFiles as $srcFile) {
                    $t = $this->createTransaction();
                    $t->setSrc(new PhingFile($srcDir, $srcFile));
                }
            }

            // process filelists
            foreach ($this->filelists as $fl) {
                $srcDir = $fl->getDir($this->project);
                $srcFiles = $fl->getFiles($this->project);
                // Make a transaction for each file
                foreach ($srcFiles as $srcFile) {
                    $t = $this->createTransaction();
                    $t->setSrc(new PhingFile($srcDir, $srcFile));
                }
            }

            // Make a transaction group for the outer command
            $t = $this->createTransaction();
            if ($this->srcFile) {
                $t->setSrc($this->srcFile);
            }
            $t->addText($this->sqlCommand);
            $this->conn = $this->getConnection();

            try {

                $this->statement = null;

                // Initialize the formatters.
                $this->initFormatters();

                try {

                    // Process all transactions
                    for ($i = 0, $size = count($this->transactions); $i < $size; $i++) {
                        if (!$this->isAutocommit()) {
                            $this->log("Beginning transaction", Project::MSG_VERBOSE);
                            $this->conn->beginTransaction();
                        }
                        $this->transactions[$i]->runTransaction();
                        if (!$this->isAutocommit()) {
                            $this->log("Committing transaction", Project::MSG_VERBOSE);
                            $this->conn->commit();
                        }
                    }
                } catch (Exception $e) {
                    $this->closeConnection();
                    throw $e;
                }
            } catch (IOException $e) {
                if (!$this->isAutocommit() && $this->conn !== null && $this->onError == "abort") {
                    try {
                        $this->conn->rollback();
                    } catch (PDOException $ex) {
                    }
                }
                $this->closeConnection();
                throw new BuildException($e->getMessage(), $this->location);
            } catch (PDOException $e) {
                if (!$this->isAutocommit() && $this->conn !== null && $this->onError == "abort") {
                    try {
                        $this->conn->rollback();
                    } catch (PDOException $ex) {
                    }
                }
                $this->closeConnection();
                throw new BuildException($e->getMessage(), $this->location);
            }

            // Close the formatters.
            $this->closeFormatters();

            $this->log(
                $this->goodSql . " of " . $this->totalSql .
                " SQL statements executed successfully"
            );

        } catch (Exception $e) {
            $this->transactions = $savedTransaction;
            $this->sqlCommand = $savedSqlCommand;
            $this->closeConnection();
            throw $e;
        }
        // finally {
        $this->transactions = $savedTransaction;
        $this->sqlCommand = $savedSqlCommand;
        $this->closeConnection();
    }

    /**
     * read in lines and execute them
     * @param Reader $reader
     * @throws BuildException
     */
    public function runStatements(Reader $reader)
    {

        if (self::DELIM_NONE == $this->delimiterType) {
            require_once 'phing/tasks/ext/pdo/DummyPDOQuerySplitter.php';
            $splitter = new DummyPDOQuerySplitter($this, $reader);
        } elseif (self::DELIM_NORMAL == $this->delimiterType && 0 === strpos($this->getUrl(), 'pgsql:')) {
            require_once 'phing/tasks/ext/pdo/PgsqlPDOQuerySplitter.php';
            $splitter = new PgsqlPDOQuerySplitter($this, $reader);
        } else {
            require_once 'phing/tasks/ext/pdo/DefaultPDOQuerySplitter.php';
            $splitter = new DefaultPDOQuerySplitter($this, $reader, $this->delimiterType);
        }

        try {
            while (null !== ($query = $splitter->nextQuery())) {
                $this->log("SQL: " . $query, Project::MSG_VERBOSE);
                $this->execSQL($query);
            }

        } catch (PDOException $e) {
            throw $e;
        }
    }

    /**
     * Whether the passed-in SQL statement is a SELECT statement.
     * This does a pretty simple match, checking to see if statement starts with
     * 'select' (but not 'select into').
     *
     * @param  string  $sql
     *
     * @return boolean Whether specified SQL looks like a SELECT query.
     */
    protected function isSelectSql($sql)
    {
        $sql = trim($sql);

        return (stripos($sql, 'select') === 0 && stripos($sql, 'select into ') !== 0);
    }

    /**
     * Exec the sql statement.
     *
     * @param $sql
     *
     * @throws BuildException
     * @throws Exception
     */
    protected function execSQL($sql)
    {
        // Check and ignore empty statements
        if (trim($sql) == "") {
            return;
        }

        try {
            $this->totalSql++;

            $this->statement = $this->conn->prepare($sql);
            $this->statement->execute();
            $this->log($this->statement->rowCount() . " rows affected", Project::MSG_VERBOSE);

            // only call processResults() for statements that return actual data (such as 'select')
            if ($this->statement->columnCount() > 0) {
                $this->processResults();
            }

            $this->statement->closeCursor();
            $this->statement = null;

            $this->goodSql++;

        } catch (PDOException $e) {
            $this->log("Failed to execute: " . $sql, Project::MSG_ERR);
            if ($this->onError != "continue") {
                throw new BuildException("Failed to execute SQL", $e);
            }
            $this->log($e->getMessage(), Project::MSG_ERR);
        }
    }

    /**
     * Returns configured PDOResultFormatter objects
     * (which were created from PDOSQLExecFormatterElement objects).
     *
     * @return array PDOResultFormatter[]
     */
    protected function getConfiguredFormatters()
    {
        $formatters = array();
        foreach ($this->formatters as $fe) {
            $formatters[] = $fe->getFormatter();
        }

        return $formatters;
    }

    /**
     * Initialize the formatters.
     */
    protected function initFormatters()
    {
        $formatters = $this->getConfiguredFormatters();
        foreach ($formatters as $formatter) {
            $formatter->initialize();
        }

    }

    /**
     * Run cleanup and close formatters.
     */
    protected function closeFormatters()
    {
        $formatters = $this->getConfiguredFormatters();
        foreach ($formatters as $formatter) {
            $formatter->close();
        }
    }

    /**
     * Passes results from query to any formatters.
     *
     * @throws PDOException
     */
    protected function processResults()
    {

        try {

            $this->log("Processing new result set.", Project::MSG_VERBOSE);

            $formatters = $this->getConfiguredFormatters();

            while ($row = $this->statement->fetch($this->fetchMode)) {
                foreach ($formatters as $formatter) {
                    $formatter->processRow($row);
                }
            }

        } catch (Exception $x) {
            $this->log("Error processing reults: " . $x->getMessage(), Project::MSG_ERR);
            foreach ($formatters as $formatter) {
                $formatter->close();
            }
            throw $x;
        }

    }

    /**
     * Closes current connection
     */
    protected function closeConnection()
    {
        if ($this->conn) {
            unset($this->conn);
            $this->conn = null;
        }
    }
}

/**
 * "Inner" class that contains the definition of a new transaction element.
 * Transactions allow several files or blocks of statements
 * to be executed using the same JDBC connection and commit
 * operation in between.
 *
 * @package   phing.tasks.ext.pdo
 */
class PDOSQLExecTransaction
{

    private $tSrcFile = null;
    private $tSqlCommand = "";
    private $parent;

    /**
     * @param $parent
     */
    public function __construct($parent)
    {
        // Parent is required so that we can log things ...
        $this->parent = $parent;
    }

    /**
     * @param PhingFile $src
     */
    public function setSrc(PhingFile $src)
    {
        $this->tSrcFile = $src;
    }

    /**
     * @param $sql
     */
    public function addText($sql)
    {
        $this->tSqlCommand .= $sql;
    }

    /**
     * @throws IOException, PDOException
     */
    public function runTransaction()
    {
        if (!empty($this->tSqlCommand)) {
            $this->parent->log("Executing commands", Project::MSG_INFO);
            $this->parent->runStatements(new StringReader($this->tSqlCommand));
        }

        if ($this->tSrcFile !== null) {
            $this->parent->log(
                "Executing file: " . $this->tSrcFile->getAbsolutePath(),
                Project::MSG_INFO
            );
            $reader = new FileReader($this->tSrcFile);
            $this->parent->runStatements($reader);
            $reader->close();
        }
    }
}
<?php

/*
 *  $Id: ff221a5d49c565473f836701865b39a315acddb9 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/types/Reference.php';

/**
 * Handles PDO configuration needed by SQL type tasks.
 *
 * @author    Hans Lellelid <hans@xmpl.org> (Phing)
 * @author    Nick Chalko <nick@chalko.com> (Ant)
 * @author    Jeff Martin <jeff@custommonkey.org> (Ant)
 * @author    Michael McCallum <gholam@xtra.co.nz> (Ant)
 * @author    Tim Stephenson <tim.stephenson@sybase.com> (Ant)
 * @version   $Id: ff221a5d49c565473f836701865b39a315acddb9 $
 * @package   phing.tasks.system
 */
abstract class PDOTask extends Task
{

    private $caching = true;

    /**
     * Autocommit flag. Default value is false
     */
    private $autocommit = false;

    /**
     * DB url.
     */
    private $url;

    /**
     * User name.
     */
    private $userId;

    /**
     * Password
     */
    private $password;

    /**
     * RDBMS Product needed for this SQL.
     **/
    private $rdbms;

    /**
     * Initialize the PDOTask
     * This method checks if the PDO classes are available and triggers
     * appropriate error if they cannot be found.  This is not done in header
     * because we may want this class to be loaded w/o triggering an error.
     */
    public function init()
    {
        if (!class_exists('PDO')) {
            throw new Exception("PDOTask depends on PDO feature being included in PHP.");
        }
    }

    /**
     * Caching loaders / driver. This is to avoid
     * getting an OutOfMemoryError when calling this task
     * multiple times in a row; default: true
     * @param $enable
     */
    public function setCaching($enable)
    {
        $this->caching = $enable;
    }

    /**
     * Sets the database connection URL; required.
     * @param The $url
     * @internal param The $url url to set
     */
    public function setUrl($url)
    {
        $this->url = $url;
    }

    /**
     * Sets the password; required.
     * @param The $password
     * @internal param The $password password to set
     */
    public function setPassword($password)
    {
        $this->password = $password;
    }

    /**
     * Auto commit flag for database connection;
     * optional, default false.
     * @param The $autocommit
     * @internal param The $autocommit autocommit to set
     */
    public function setAutocommit($autocommit)
    {
        $this->autocommit = $autocommit;
    }

    /**
     * Sets the version string, execute task only if
     * rdbms version match; optional.
     * @param The $version
     * @internal param The $version version to set
     */
    public function setVersion($version)
    {
        $this->version = $version;
    }

    /**
     * @return mixed
     */
    protected function getLoaderMap()
    {
        return self::$loaderMap;
    }

    /**
     * Creates a new Connection as using the driver, url, userid and password specified.
     * The calling method is responsible for closing the connection.
     * @return Connection     the newly created connection.
     * @throws BuildException if the UserId/Password/Url is not set or there is no suitable driver or the driver fails to load.
     */
    protected function getConnection()
    {

        if ($this->url === null) {
            throw new BuildException("Url attribute must be set!", $this->location);
        }

        try {

            $this->log("Connecting to " . $this->getUrl(), Project::MSG_VERBOSE);

            $user = null;
            $pass = null;

            if ($this->userId) {
                $user = $this->getUserId();
            }

            if ($this->password) {
                $pass = $this->getPassword();
            }

            $conn = new PDO($this->getUrl(), $user, $pass);
            $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

            try {
                $conn->setAttribute(PDO::ATTR_AUTOCOMMIT, $this->autocommit);
            } catch (PDOException $pe) {
                $this->log(
                    "Unable to enable auto-commit for this database: " . $pe->getMessage(),
                    Project::MSG_VERBOSE
                );
            }

            return $conn;

        } catch (PDOException $e) {
            throw new BuildException($e->getMessage(), $this->location);
        }

    }

    /**
     * @param $value
     */
    public function isCaching($value)
    {
        $this->caching = $value;
    }

    /**
     * Gets the autocommit.
     * @return Returns a boolean
     */
    public function isAutocommit()
    {
        return $this->autocommit;
    }

    /**
     * Gets the url.
     * @return string
     */
    public function getUrl()
    {
        return $this->url;
    }

    /**
     * Gets the userId.
     * @return string
     */
    public function getUserId()
    {
        return $this->userId;
    }

    /**
     * Set the user name for the connection; required.
     * @param string $userId
     */
    public function setUserid($userId)
    {
        $this->userId = $userId;
    }

    /**
     * Gets the password.
     * @return string
     */
    public function getPassword()
    {
        return $this->password;
    }
}
<?php

/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 *
 * @version SVN: $Id: 2e4cf5659024930ba454c239c5e288e2fd7dab37 $
 * @package phing.tasks.ext.pdo
 */

require_once 'phing/tasks/ext/pdo/PDOQuerySplitter.php';

/**
 * Splits PostgreSQL's dialect of SQL into separate queries
 *
 * Unlike DefaultPDOQuerySplitter this uses a lexer instead of regular
 * expressions. This allows handling complex constructs like C-style comments
 * (including nested ones) and dollar-quoted strings.
 *
 * @author  Alexey Borzov <avb@php.net>
 * @package phing.tasks.ext.pdo
 * @version $Id: 2e4cf5659024930ba454c239c5e288e2fd7dab37 $
 * @link    http://www.phing.info/trac/ticket/499
 * @link    http://www.postgresql.org/docs/current/interactive/sql-syntax-lexical.html#SQL-SYNTAX-DOLLAR-QUOTING
 */
class PgsqlPDOQuerySplitter extends PDOQuerySplitter
{
    /**#@+
     * Lexer states
     */
    const STATE_NORMAL = 0;
    const STATE_SINGLE_QUOTED = 1;
    const STATE_DOUBLE_QUOTED = 2;
    const STATE_DOLLAR_QUOTED = 3;
    const STATE_COMMENT_LINEEND = 4;
    const STATE_COMMENT_MULTILINE = 5;
    const STATE_BACKSLASH = 6;
    /**#@-*/

    /**
     * Nesting depth of current multiline comment
     * @var int
     */
    protected $commentDepth = 0;

    /**
     * Current dollar-quoting "tag"
     * @var string
     */
    protected $quotingTag = '';

    /**
     * Current lexer state, one of STATE_* constants
     * @var int
     */
    protected $state = self::STATE_NORMAL;

    /**
     * Whether a backslash was just encountered in quoted string
     * @var bool
     */
    protected $escape = false;

    /**
     * Current source line being examined
     * @var string
     */
    protected $line = '';

    /**
     * Position in current source line
     * @var int
     */
    protected $inputIndex;

    /**
     * Gets next symbol from the input, false if at end
     *
     * @return string|bool
     */
    public function getc()
    {
        if (!strlen($this->line) || $this->inputIndex >= strlen($this->line)) {
            if (null === ($line = $this->sqlReader->readLine())) {
                return false;
            }
            $project = $this->parent->getOwningTarget()->getProject();
            $this->line = ProjectConfigurator::replaceProperties(
                    $project,
                    $line,
                    $project->getProperties()
                ) . "\n";
            $this->inputIndex = 0;
        }

        return $this->line[$this->inputIndex++];
    }

    /**
     * Bactracks one symbol on the input
     *
     * NB: we don't need ungetc() at the start of the line, so this case is
     * not handled.
     */
    public function ungetc()
    {
        $this->inputIndex--;
    }

    /**
     * Checks whether symbols after $ are a valid dollar-quoting tag
     *
     * @return string|bool Dollar-quoting "tag" if it is present, false otherwise
     */
    protected function checkDollarQuote()
    {
        $ch = $this->getc();
        if ('$' == $ch) {
            // empty tag
            return '';

        } elseif (!ctype_alpha($ch) && '_' != $ch) {
            // not a delimiter
            $this->ungetc();

            return false;

        } else {
            $tag = $ch;
            while (false !== ($ch = $this->getc())) {
                if ('$' == $ch) {
                    return $tag;

                } elseif (ctype_alnum($ch) || '_' == $ch) {
                    $tag .= $ch;

                } else {
                    for ($i = 0; $i < strlen($tag); $i++) {
                        $this->ungetc();
                    }

                    return false;
                }
            }
        }
    }

    /**
     * @return null|string
     */
    public function nextQuery()
    {
        $sql = '';
        $delimiter = $this->parent->getDelimiter();
        $openParens = 0;

        while (false !== ($ch = $this->getc())) {
            switch ($this->state) {
                case self::STATE_NORMAL:
                    switch ($ch) {
                        case '-':
                            if ('-' == $this->getc()) {
                                $this->state = self::STATE_COMMENT_LINEEND;
                            } else {
                                $this->ungetc();
                            }
                            break;
                        case '"':
                            $this->state = self::STATE_DOUBLE_QUOTED;
                            break;
                        case "'":
                            $this->state = self::STATE_SINGLE_QUOTED;
                            break;
                        case '/':
                            if ('*' == $this->getc()) {
                                $this->state = self::STATE_COMMENT_MULTILINE;
                                $this->commentDepth = 1;
                            } else {
                                $this->ungetc();
                            }
                            break;
                        case '$':
                            if (false !== ($tag = $this->checkDollarQuote())) {
                                $this->state = self::STATE_DOLLAR_QUOTED;
                                $this->quotingTag = $tag;
                                $sql .= '$' . $tag . '$';
                                continue 3;
                            }
                            break;
                        case '(':
                            $openParens++;
                            break;
                        case ')':
                            $openParens--;
                            break;
                        // technically we can use e.g. psql's \g command as delimiter
                        case $delimiter[0]:
                            // special case to allow "create rule" statements
                            // http://www.postgresql.org/docs/current/interactive/sql-createrule.html
                            if (';' == $delimiter && 0 < $openParens) {
                                break;
                            }
                            $hasQuery = true;
                            for ($i = 1; $i < strlen($delimiter); $i++) {
                                if ($delimiter[$i] != $this->getc()) {
                                    $hasQuery = false;
                                }
                            }
                            if ($hasQuery) {
                                return $sql;
                            } else {
                                for ($j = 1; $j < $i; $j++) {
                                    $this->ungetc();
                                }
                            }
                    }
                    break;

                case self::STATE_COMMENT_LINEEND:
                    if ("\n" == $ch) {
                        $this->state = self::STATE_NORMAL;
                    }
                    break;

                case self::STATE_COMMENT_MULTILINE:
                    switch ($ch) {
                        case '/':
                            if ('*' != $this->getc()) {
                                $this->ungetc();
                            } else {
                                $this->commentDepth++;
                            }
                            break;

                        case '*':
                            if ('/' != $this->getc()) {
                                $this->ungetc();
                            } else {
                                $this->commentDepth--;
                                if (0 == $this->commentDepth) {
                                    $this->state = self::STATE_NORMAL;
                                    continue 3;
                                }
                            }
                    }

                case self::STATE_SINGLE_QUOTED:
                case self::STATE_DOUBLE_QUOTED:
                    if ($this->escape) {
                        $this->escape = false;
                        break;
                    }
                    $quote = $this->state == self::STATE_SINGLE_QUOTED ? "'" : '"';
                    switch ($ch) {
                        case '\\':
                            $this->escape = true;
                            break;
                        case $quote:
                            if ($quote == $this->getc()) {
                                $sql .= $quote;
                            } else {
                                $this->ungetc();
                                $this->state = self::STATE_NORMAL;
                            }
                    }

                case self::STATE_DOLLAR_QUOTED:
                    if ('$' == $ch && false !== ($tag = $this->checkDollarQuote())) {
                        if ($tag == $this->quotingTag) {
                            $this->state = self::STATE_NORMAL;
                        }
                        $sql .= '$' . $tag . '$';
                        continue 2;
                    }
            }

            if ($this->state != self::STATE_COMMENT_LINEEND && $this->state != self::STATE_COMMENT_MULTILINE) {
                $sql .= $ch;
            }
        }
        if ('' !== $sql) {
            return $sql;
        }

        return null;
    }
}
<?php
/**
 * $Id: 66f8aebc32cb36b19ab6a1d536d19ce4fba1dc1c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/PhingFile.php';
require_once 'phing/tasks/ext/pdo/PDOResultFormatter.php';

/**
 * Plain text formatter for PDO results.
 *
 * @author Hans Lellelid <hans@xmpl.org>
 * @package phing.tasks.ext.pdo
 * @since 2.3.0
 */
class PlainPDOResultFormatter extends PDOResultFormatter
{
    /**
     * Have column headers been printed?
     * @var boolean
     */
    private $colsprinted = false;

    /**
     * Whether to show headers.
     * @var boolean
     */
    private $showheaders = true;

    /**
     * Column delimiter.
     * Defaults to ','
     * @var string
     */
    private $coldelimiter = ",";

    /**
     * Row delimiter.
     * Defaults to PHP_EOL.
     * @var string
     */
    private $rowdelimiter = PHP_EOL;

    /**
     * Set the showheaders attribute.
     * @param boolean $v
     */
    public function setShowheaders($v)
    {
        $this->showheaders = StringHelper::booleanValue($v);
    }

    /**
     * Sets the column delimiter.
     * @param string $v
     */
    public function setColdelim($v)
    {
        $this->coldelimiter = $v;
    }

    /**
     * Sets the row delimiter.
     * @param string $v
     */
    public function setRowdelim($v)
    {
        $this->rowdelimiter = $v;
    }

    /**
     * Processes a specific row from PDO result set.
     *
     * @param array $row Row of PDO result set.
     */
    public function processRow($row)
    {

        if (!$this->colsprinted && $this->showheaders) {
            $first = true;
            foreach ($row as $fieldName => $ignore) {
                if ($first) {
                    $first = false;
                } else {
                    $line .= ",";
                }
                $line .= $fieldName;
            }

            $this->out->write($line);
            $this->out->write(PHP_EOL);

            $line = "";
            $colsprinted = true;
        } // if show headers

        $first = true;
        foreach ($row as $columnValue) {

            if ($columnValue != null) {
                $columnValue = trim($columnValue);
            }

            if ($first) {
                $first = false;
            } else {
                $line .= $this->coldelimiter;
            }
            $line .= $columnValue;
        }

        $this->out->write($line);
        $this->out->write($this->rowdelimiter);

    }

    /**
     * @return PhingFile
     */
    public function getPreferredOutfile()
    {
        return new PhingFile('results.txt');
    }

}
<?php
/**
 * $Id: d26c94795e93844f84a602ed31361de6606989ed $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/PhingFile.php';
require_once 'phing/tasks/ext/pdo/PDOResultFormatter.php';

/**
 * XML formatter for PDO results.
 *
 * This class reprsents the output of a query using a simple XML schema.
 *
 * <results>
 *  <row>
 *   <col name="id">value</col>
 *   <col name="name">value2</col>
 *  </row>
 *  <row>
 *   <col name="id">value</col>
 *   <col name="name">value2</col>
 *  </row>
 * </results>
 *
 * The actual names of the colums will depend on the fetchmode that was used
 * with PDO.
 *
 * @author Hans Lellelid <hans@xmpl.org>
 * @package phing.tasks.ext.pdo
 * @since 2.3.0
 */
class XMLPDOResultFormatter extends PDOResultFormatter
{

    /**
     * The XML document being created.
     * @var DOMDocument
     */
    private $doc;

    /**
     * @var DOMElement
     */
    private $rootNode;

    /**
     * XML document encoding
     *
     * @var string
     */
    private $encoding;

    /**
     * @var boolean
     */
    private $formatOutput = true;

    /**
     * Set the DOM document encoding.
     * @param string $v
     */
    public function setEncoding($v)
    {
        $this->encoding = $v;
    }

    /**
     * @param boolean $v
     */
    public function setFormatOutput($v)
    {
        $this->formatOutput = (boolean) $v;
    }

    public function initialize()
    {
        $this->doc = new DOMDocument("1.0", $this->encoding);
        $this->rootNode = $this->doc->createElement('results');
        $this->doc->appendChild($this->rootNode);
        $this->doc->formatOutput = $this->formatOutput;
    }

    /**
     * Processes a specific row from PDO result set.
     *
     * @param array $row Row of PDO result set.
     */
    public function processRow($row)
    {

        $rowNode = $this->doc->createElement('row');
        $this->rootNode->appendChild($rowNode);

        foreach ($row as $columnName => $columnValue) {

            $colNode = $this->doc->createElement('column');
            $colNode->setAttribute('name', $columnName);

            if ($columnValue != null) {
                $columnValue = trim($columnValue);
                $colNode->nodeValue = $columnValue;
            }
            $rowNode->appendChild($colNode);
        }

    }

    /**
     * Gets a preferred filename for an output file.
     *
     * If no filename is specified, this is where the results will be placed
     * (unless usefile=false).
     *
     * @return string
     */
    public function getPreferredOutfile()
    {
        return new PhingFile('results.xml');
    }

    /**
     * Write XML to file and free the DOM objects.
     */
    public function close()
    {
        $this->out->write($this->doc->saveXML());
        $this->rootNode = null;
        $this->doc = null;
        parent::close();
    }
}
<?php
/*
 *  $Id: 4287f56d47c2d793bc22417402f8c37daf3745f3 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/system/io/PhingFile.php';

/**
 * Builds list of files for PEAR_PackageFileManager using a Phing FileSet.
 *
 * Some code here is taken from PEAR_PackageFileManager_File -- getting results from flat
 * array into the assoc array expected from getFileList().
 *
 * @author   Greg Beaver
 * @author   Hans Lellelid <hans@xmpl.org>
 * @package  phing.tasks.ext.pearpackage
 * @version  $Id: 4287f56d47c2d793bc22417402f8c37daf3745f3 $
 */
class PEAR_PackageFileManager_Fileset
{

    /**
     * Current Phing Project.
     * @var Project
     */
    private $project;

    /**
     * FileSets to use.
     * @var array FileSet[]
     */
    private $filesets = array();

    /**
     * Set up the FileSet filelist generator
     *
     * 'project' and 'filesets' are the only options that this class uses.
     *
     * @param PEAR_PackageFileManager
     * @param array
     */
    public function __construct($options)
    {
        if (!is_array($options)) {
            $options = $options->getOptions();
        }

        $this->project = $options['phing_project'];
        $this->filesets = $options['phing_filesets'];
    }

    /**
     * Generate the <filelist></filelist> section
     * of the package file.
     *
     * This function performs the backend generation of the array
     * containing all files in this package
     * @return array structure of all files to include
     */
    public function getFileList()
    {

        $allfiles = array();

        foreach ($this->filesets as $fs) {
            $ds = $fs->getDirectoryScanner($this->project);

            $files = $ds->getIncludedFiles();

            // We need to store these files keyed by the basedir from DirectoryScanner
            // so that we can resolve the fullpath of the file later.
            if (isset($allfiles[$ds->getBasedir()])) {
                $allfiles[$ds->getBasedir()] = array_merge($allfiles[$ds->getBasedir()], $files);
            } else {
                $allfiles[$ds->getBasedir()] = $files;
            }
        }

        $struc = array();

        foreach ($allfiles as $basedir => $files) {

            foreach ($files as $file) {

                // paths are relative to $basedir above
                $path = strtr(dirname($file), DIRECTORY_SEPARATOR, '/');

                if (!$path || $path == '.') {
                    $path = '/'; // for array index
                }

                $parts = explode('.', basename($file));
                $ext = array_pop($parts);
                if (strlen($ext) == strlen($file)) {
                    $ext = '';
                }

                $f = new PhingFile($basedir, $file);

                $struc[$path][] = array(
                    'file' => basename($file),
                    'ext' => $ext,
                    'path' => (($path == '/') ? basename($file) : $path . '/' . basename($file)),
                    'fullpath' => $f->getAbsolutePath()
                );
            }
        }

        uksort($struc, 'strnatcasecmp');
        foreach ($struc as $key => $ind) {
            usort($ind, array($this, 'sortfiles'));
            $struc[$key] = $ind;
        }

        $tempstruc = $struc;
        $struc = array('/' => $tempstruc['/']);
        $bv = 0;
        foreach ($tempstruc as $key => $ind) {
            $save = $key;
            if ($key != '/') {
                $struc['/'] = $this->setupDirs($struc['/'], explode('/', $key), $tempstruc[$key]);
            }
        }
        uksort($struc['/'], array($this, 'mystrucsort'));

        return $struc;
    }

    /**
     * Recursively move contents of $struc into associative array
     *
     * The contents of $struc have many indexes like 'dir/subdir/subdir2'.
     * This function converts them to
     * array('dir' => array('subdir' => array('subdir2')))
     * @param array $struc is array('dir' => array of files in dir,
     *              'dir/subdir' => array of files in dir/subdir,...)
     * @param $dir
     * @param $contents
     * @internal param array $array form of 'dir/subdir/subdir2' array('dir','subdir','subdir2')
     * @return array same as struc but with array('dir' =>
     *               array(file1,file2,'subdir' => array(file1,...)))
     */
    private function setupDirs($struc, $dir, $contents)
    {

        if (!count($dir)) {
            foreach ($contents as $dir => $files) {
                if (is_string($dir)) {
                    if (strpos($dir, '/')) {
                        $test = true;
                        $a = $contents[$dir];
                        unset($contents[$dir]);
                        $b = explode('/', $dir);
                        $c = array_shift($b);
                        if (isset($contents[$c])) {
                            $contents[$c] = $this->setDir($contents[$c], $this->setupDirs(array(), $b, $a));
                        } else {
                            $contents[$c] = $this->setupDirs(array(), $b, $a);
                        }
                    }
                }
            }

            return $contents;
        }
        $me = array_shift($dir);
        if (!isset($struc[$me])) {
            $struc[$me] = array();
        }
        $struc[$me] = $this->setupDirs($struc[$me], $dir, $contents);

        return $struc;
    }

    /**
     * Recursively add all the subdirectories of $contents to $dir without erasing anything in
     * $dir
     * @param array
     * @param array
     * @return array processed $dir
     */
    public function setDir($dir, $contents)
    {
        while (list($one, $two) = each($contents)) {
            if (isset($dir[$one])) {
                $dir[$one] = $this->setDir($dir[$one], $contents[$one]);
            } else {
                $dir[$one] = $two;
            }
        }

        return $dir;
    }

    /**
     * Sorting functions for the file list
     * @param string
     * @param string
     * @return int
     */
    private function sortfiles($a, $b)
    {
        return strnatcasecmp($a['file'], $b['file']);
    }

    /**
     * @param $a
     * @param $b
     * @return int
     */
    private function mystrucsort($a, $b)
    {
        if (is_numeric($a) && is_string($b)) {
            return 1;
        }
        if (is_numeric($b) && is_string($a)) {
            return -1;
        }
        if (is_numeric($a) && is_numeric($b)) {
            if ($a > $b) {
                return 1;
            }
            if ($a < $b) {
                return -1;
            }
            if ($a == $b) {
                return 0;
            }
        }

        return strnatcasecmp($a, $b);
    }
}
<?php
/*
 *  $Id: 2d0f1f7fa10c416782647339da7fc5d26780d911 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/PearPackageTask.php';

/**
 * A task to create a PEAR package.xml version 2.0 file.
 *
 * This class uses the PEAR_PackageFileManager2 class to perform the work.
 *
 * This class is designed to be very flexible -- i.e. account for changes to the package.xml w/o
 * requiring changes to this class.  We've accomplished this by having generic <option> and <mapping>
 * nested elements.  All options are set using PEAR_PackageFileManager2::setOptions().
 *
 * The <option> tag is used to set a simple option value.
 * <code>
 * <option name="option_name" value="option_value"/>
 * or <option name="option_name">option_value</option>
 * </code>
 *
 * The <mapping> tag represents a complex data type.  You can use nested <element> (and nested <element> with
 * <element> tags) to represent the full complexity of the structure.  Bear in mind that what you are creating
 * will be mapped to an associative array that will be passed in via PEAR_PackageFileManager2::setOptions().
 * <code>
 * <mapping name="option_name">
 *  <element key="key_name" value="key_val"/>
 *  <element key="key_name" value="key_val"/>
 * </mapping>
 * </code>
 *
 * Here's an over-simple example of how this could be used:
 * <code>
 * <pearpkg2 name="phing" dir="${build.src.dir}">
 *  <fileset dir="src">
 *      <include name="**"/>
 *  </fileset>
 *  <option name="outputdirectory" value="./build"/>
 *  <option name="packagefile" value="package2.xml"/>
 *  <option name="packagedirectory" value="./${build.dist.dir}"/>
 *  <option name="baseinstalldir" value="${pkg.prefix}"/>
 *  <option name="channel" value="my.pear-channel.com"/>
 *  <option name="summary" value="${pkg.summary}"/>
 *  <option name="description" value="${pkg.description}"/>
 *  <option name="apiversion" value="${pkg.version}"/>
 *  <option name="apistability" value="beta"/>
 *  <option name="releaseversion" value="${pkg.version}"/>
 *  <option name="releasestability" value="beta"/>
 *  <option name="license" value="none"/>
 *  <option name="phpdep" value="5.0.0"/>
 *  <option name="pearinstallerdep" value="1.4.6"/>
 *  <option name="packagetype" value="php"/>
 *  <option name="notes" value="${pkg.relnotes}"/>
 *  <mapping name="maintainers">
 *   <element>
 *    <element key="handle" value="hlellelid"/>
 *    <element key="name" value="Hans"/>
 *    <element key="email" value="hans@xmpl.org"/>
 *    <element key="role" value="lead"/>
 *    <element key="active" value="yes"/>
 *   </element>
 *  </mapping>
 * </pearpkg2>
 * </code>
 *
 * Look at the build.xml in the Phing base directory (assuming you have the full distro / CVS version of Phing) to
 * see a more complete example of how to call this script.
 *
 * @author   Stuart Binge <stuart.binge@complinet.com>
 * @author   Hans Lellelid <hans@xmpl.org>
 * @package  phing.tasks.ext
 * @version  $Id: 2d0f1f7fa10c416782647339da7fc5d26780d911 $
 */
class PearPackage2Task extends PearPackageTask
{

    public function init()
    {
        include_once 'PEAR/PackageFileManager2.php';
        if (!class_exists('PEAR_PackageFileManager2')) {
            throw new BuildException("You must have installed PEAR_PackageFileManager in order to create a PEAR package.xml version 2.0 file.");
        }
    }

    protected function setVersion2Options()
    {
        $this->pkg->setPackage($this->package);
        $this->pkg->setDate(strftime('%Y-%m-%d'));
        $this->pkg->setTime(strftime('%H:%M:%S'));

        $newopts = array();
        foreach ($this->options as $opt) {
            switch ($opt->getName()) {
                case 'summary':
                    $this->pkg->setSummary($opt->getValue());
                    break;

                case 'description':
                    $this->pkg->setDescription($opt->getValue());
                    break;

                case 'uri':
                    $this->pkg->setUri($opt->getValue());
                    break;

                case 'license':
                    $this->pkg->setLicense($opt->getValue());
                    break;

                case 'channel':
                    $this->pkg->setChannel($opt->getValue());
                    break;

                case 'apiversion':
                    $this->pkg->setAPIVersion($opt->getValue());
                    break;

                case 'releaseversion':
                    $this->pkg->setReleaseVersion($opt->getValue());
                    break;

                case 'releasestability':
                    $this->pkg->setReleaseStability($opt->getValue());
                    break;

                case 'apistability':
                    $this->pkg->setAPIStability($opt->getValue());
                    break;

                case 'notes':
                    $this->pkg->setNotes($opt->getValue());
                    break;

                case 'packagetype':
                    $this->pkg->setPackageType($opt->getValue());
                    break;

                case 'phpdep':
                    $this->pkg->setPhpDep($opt->getValue());
                    break;

                case 'pearinstallerdep':
                    $this->pkg->setPearinstallerDep($opt->getValue());
                    break;

                default:
                    $newopts[] = $opt;
                    break;
            }
        }
        $this->options = $newopts;

        $newmaps = array();
        foreach ($this->mappings as $map) {
            switch ($map->getName()) {
                case 'deps':
                    $deps = $map->getValue();
                    foreach ($deps as $dep) {
                        $type = isset($dep['optional']) ? 'optional' : 'required';
                        $min = isset($dep['min']) ? $dep['min'] : $dep['version'];
                        $max = isset($dep['max']) ? $dep['max'] : null;
                        $rec = isset($dep['recommended']) ? $dep['recommended'] : null;
                        $channel = isset($dep['channel']) ? $dep['channel'] : false;
                        $uri = isset($dep['uri']) ? $dep['uri'] : false;

                        if (!empty($channel)) {
                            $this->pkg->addPackageDepWithChannel(
                                $type,
                                $dep['name'],
                                $channel,
                                $min,
                                $max,
                                $rec
                            );
                        } elseif (!empty($uri)) {
                            $this->pkg->addPackageDepWithUri(
                                $type,
                                $dep['name'],
                                $uri
                            );
                        }
                    };
                    break;

                case 'extdeps':
                    $deps = $map->getValue();
                    foreach ($deps as $dep) {
                        $type = isset($dep['optional']) ? 'optional' : 'required';
                        $min = isset($dep['min']) ? $dep['min'] : $dep['version'];
                        $max = isset($dep['max']) ? $dep['max'] : $dep['version'];
                        $rec = isset($dep['recommended']) ? $dep['recommended'] : $dep['version'];

                        $this->pkg->addExtensionDep(
                            $type,
                            $dep['name'],
                            $min,
                            $max,
                            $rec
                        );
                    };
                    break;

                case 'maintainers':
                    $maintainers = $map->getValue();

                    foreach ($maintainers as $maintainer) {
                        if (!isset($maintainer['active'])) {
                            $maintainer['active'] = 'yes';
                        } else {
                            $maintainer['active'] = $maintainer['active'] === false ? 'no' : 'yes';
                        }
                        $this->pkg->addMaintainer(
                            $maintainer['role'],
                            $maintainer['handle'],
                            $maintainer['name'],
                            $maintainer['email'],
                            $maintainer['active']
                        );
                    }
                    break;

                case 'replacements':
                    $replacements = $map->getValue();

                    foreach ($replacements as $replacement) {
                        $this->pkg->addReplacement(
                            $replacement['path'],
                            $replacement['type'],
                            $replacement['from'],
                            $replacement['to']
                        );
                    }
                    break;

                case 'role':
                    foreach ($map->getValue() as $role) {
                        $this->pkg->addRole($role['extension'], $role['role']);
                    }
                    break;

                default:
                    $newmaps[] = $map;
            }
        }
        $this->mappings = $newmaps;
    }

    /**
     * Main entry point.
     * @throws BuildException
     * @return void
     */
    public function main()
    {
        if ($this->dir === null) {
            throw new BuildException("You must specify the \"dir\" attribute for PEAR package 2 task.");
        }

        if ($this->package === null) {
            throw new BuildException("You must specify the \"name\" attribute for PEAR package 2 task.");
        }

        $this->pkg = new PEAR_PackageFileManager2();

        $this->setVersion2Options();
        $this->setOptions();

        $this->pkg->addRelease();
        $this->pkg->generateContents();
        $e = $this->pkg->writePackageFile();
        if (@PEAR::isError($e)) {
            throw new BuildException("Unable to write package file.", new Exception($e->getMessage()));
        }
    }

}
<?php
/*
 *  $Id: 11ecf61bf1d7f534232f2394df745ee147f2b9b7 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/MatchingTask.php';
include_once 'phing/types/FileSet.php';

/**
 * A task to create PEAR package.xml file.
 *
 * This class uses the PEAR_PackageFileMaintainer class to perform the work.
 *
 * This class is designed to be very flexible -- i.e. account for changes to the package.xml w/o
 * requiring changes to this class.  We've accomplished this by having generic <option> and <mapping>
 * nested elements.  All options are set using PEAR_PackageFileMaintainer::setOptions().
 *
 * The <option> tag is used to set a simple option value.
 * <code>
 * <option name="option_name" value="option_value"/>
 * or <option name="option_name">option_value</option>
 * </code>
 *
 * The <mapping> tag represents a complex data type.  You can use nested <element> (and nested <element> with
 * <element> tags) to represent the full complexity of the structure.  Bear in mind that what you are creating
 * will be mapped to an associative array that will be passed in via PEAR_PackageFileMaintainer::setOptions().
 * <code>
 * <mapping name="option_name">
 *  <element key="key_name" value="key_val"/>
 *  <element key="key_name" value="key_val"/>
 * </mapping>
 * </code>
 *
 * Here's an over-simple example of how this could be used:
 * <code>
 * <pearpkg name="phing" dir="${build.src.dir}" destFile="${build.base.dir}/package.xml">
 *  <fileset>
 *   <include name="**"/>
 *  </fileset>
 *  <option name="notes">Sample release notes here.</option>
 *  <option name="description">Package description</option>
 *  <option name="summary">Short description</option>
 *  <option name="version" value="2.0.0b1"/>
 *  <option name="state" value="beta"/>
 *  <mapping name="maintainers">
 *   <element>
 *    <element key="handle" value="hlellelid"/>
 *    <element key="name" value="Hans"/>
 *    <element key="email" value="hans@xmpl.org"/>
 *    <element key="role" value="lead"/>
 *   </element>
 *  </mapping>
 * </pearpkg>
 * </code>
 *
 * Look at the build.xml in the Phing base directory (assuming you have the full distro / CVS version of Phing) to
 * see a more complete example of how to call this script.
 *
 * @author   Hans Lellelid <hans@xmpl.org>
 * @package  phing.tasks.ext
 * @version  $Id: 11ecf61bf1d7f534232f2394df745ee147f2b9b7 $
 */
class PearPackageTask extends MatchingTask
{

    /** */
    protected $package;

    /** Base directory for reading files. */
    protected $dir;

    /** Package file */
    private $packageFile;

    /** @var array FileSet[] */
    private $filesets = array();

    /** @var PEAR_PackageFileManager */
    protected $pkg;

    private $preparedOptions = array();

    /** @var array PearPkgOption[] */
    protected $options = array();

    /** Nested <mapping> (complex options) types. */
    protected $mappings = array();

    /**
     * Nested <role> elements
     * @var PearPkgRole[]
     */
    protected $roles = array();

    public function init()
    {
        include_once 'PEAR/PackageFileManager.php';
        if (!class_exists('PEAR_PackageFileManager')) {
            throw new BuildException("You must have installed PEAR_PackageFileManager in order to create a PEAR package.xml file.");
        }
    }

    /**
     * Sets PEAR package.xml options, based on class properties.
     * @throws BuildException
     * @return void
     */
    protected function setOptions()
    {

        // 1) first prepare/populate options
        $this->populateOptions();

        // 2) make any final adjustments (this could move into populateOptions() also)

        // default PEAR basedir would be the name of the package (e.g."phing")
        if (!isset($this->preparedOptions['baseinstalldir'])) {
            $this->preparedOptions['baseinstalldir'] = $this->package;
        }

        // unless filelistgenerator has been overridden, we use Phing FileSet generator
        if (!isset($this->preparedOptions['filelistgenerator'])) {
            if (empty($this->filesets)) {
                throw new BuildException("You must use a <fileset> tag to specify the files to include in the package.xml");
            }
            $this->preparedOptions['filelistgenerator'] = 'Fileset';
            $this->preparedOptions['usergeneratordir'] = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'pearpackage';
            // Some PHING-specific options needed by our Fileset reader
            $this->preparedOptions['phing_project'] = $this->project;
            $this->preparedOptions['phing_filesets'] = $this->filesets;
        } elseif ($this->preparedOptions['filelistgenerator'] != 'Fileset' && !empty($this->filesets)) {
            throw new BuildException("You cannot use <fileset> element if you have specified the \"filelistgenerator\" option.");
        }

        // 3) Set the options

        // No need for excessive validation here, since the  PEAR class will do its own
        // validation & return errors
        $e = $this->pkg->setOptions($this->preparedOptions);

        if (@PEAR::isError($e)) {
            throw new BuildException("Unable to set options.", new Exception($e->getMessage()));
        }

        // convert roles
        foreach ($this->roles as $role) {
            $this->pkg->addRole($role->getExtension(), $role->getRole());
        }
    }

    /**
     * Fixes the boolean in optional dependencies
     * @param $deps
     * @return
     */
    private function fixDeps($deps)
    {
        foreach (array_keys($deps) as $dep) {
            if (isset($deps[$dep]['optional']) && $deps[$dep]['optional']) {
                $deps[$dep]['optional'] = "yes";
            }
        }

        return $deps;
    }

    /**
     * Adds the options that are set via attributes and the nested tags to the options array.
     */
    private function populateOptions()
    {

        // These values could be overridden if explicitly defined using nested tags
        $this->preparedOptions['package'] = $this->package;
        $this->preparedOptions['packagedirectory'] = $this->dir->getAbsolutePath();

        if ($this->packageFile !== null) {
            // create one w/ full path
            $f = new PhingFile($this->packageFile->getAbsolutePath());
            $this->preparedOptions['packagefile'] = $f->getName();
            // must end in trailing slash
            $this->preparedOptions['outputdirectory'] = $f->getParent() . DIRECTORY_SEPARATOR;
            $this->log("Creating package file: " . $f->__toString(), Project::MSG_INFO);
        } else {
            $this->log("Creating [default] package.xml file in base directory.", Project::MSG_INFO);
        }

        // converts option objects and mapping objects into
        // key => value options that can be passed to PEAR_PackageFileManager

        foreach ($this->options as $opt) {
            $this->preparedOptions[$opt->getName()] = $opt->getValue(
            ); //no arrays yet. preg_split('/\s*,\s*/', $opt->getValue());
        }

        foreach ($this->mappings as $map) {
            $value = $map->getValue(); // getValue returns complex value

            if ($map->getName() == 'deps') {
                $value = $this->fixDeps($value);
            }

            $this->preparedOptions[$map->getName()] = $value;
        }
    }

    /**
     * Main entry point.
     * @throws BuildException
     * @return void
     */
    public function main()
    {

        if ($this->dir === null) {
            throw new BuildException("You must specify the \"dir\" attribute for PEAR package task.");
        }

        if ($this->package === null) {
            throw new BuildException("You must specify the \"name\" attribute for PEAR package task.");
        }

        $this->pkg = new PEAR_PackageFileManager();

        $this->setOptions();

        $e = $this->pkg->writePackageFile();
        if (@PEAR::isError($e)) {
            throw new BuildException("Unable to write package file.", new Exception($e->getMessage()));
        }

    }

    /**
     * Used by the PEAR_PackageFileManager_PhingFileSet lister.
     * @return array FileSet[]
     */
    public function getFileSets()
    {
        return $this->filesets;
    }

    // -------------------------------
    // Set properties from XML
    // -------------------------------

    /**
     * Nested creator, creates a FileSet for this task
     *
     * @param FileSet $fs
     * @internal param FileSet $fileset Set of files to add to the package
     *
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Set "package" property from XML.
     * @see setName()
     * @param  string $v
     * @return void
     */
    public function setPackage($v)
    {
        $this->package = $v;
    }

    /**
     * Sets "dir" property from XML.
     * @param  PhingFile $f
     * @return void
     */
    public function setDir(PhingFile $f)
    {
        $this->dir = $f;
    }

    /**
     * Sets "name" property from XML.
     * @param  string $v
     * @return void
     */
    public function setName($v)
    {
        $this->package = $v;
    }

    /**
     * Sets the file to use for generated package.xml
     * @param PhingFile $f
     */
    public function setDestFile(PhingFile $f)
    {
        $this->packageFile = $f;
    }

    /**
     * Handles nested generic <option> elements.
     */
    public function createOption()
    {
        $o = new PearPkgOption();
        $this->options[] = $o;

        return $o;
    }

    /**
     * Handles nested generic <option> elements.
     */
    public function createMapping()
    {
        $o = new PearPkgMapping();
        $this->mappings[] = $o;

        return $o;
    }

    /**
     * Handles nested <role> elements
     * @return PearPkgRole
     */
    public function createRole()
    {
        $role = new PearPkgRole();
        $this->roles[] = $role;

        return $role;
    }
}

/**
 * Generic option class is used for non-complex options.
 *
 * @package  phing.tasks.ext
 */
class PearPkgOption
{

    private $name;
    private $value;

    /**
     * @param $v
     */
    public function setName($v)
    {
        $this->name = $v;
    }

    public function getName()
    {
        return $this->name;
    }

    /**
     * @param $v
     */
    public function setValue($v)
    {
        $this->value = $v;
    }

    public function getValue()
    {
        return $this->value;
    }

    /**
     * @param $txt
     */
    public function addText($txt)
    {
        $this->value = trim($txt);
    }

}

/**
 * Handles complex options <mapping> elements which are hashes (assoc arrays).
 *
 * @package  phing.tasks.ext
 */
class PearPkgMapping
{

    private $name;
    private $elements = array();

    /**
     * @param $v
     */
    public function setName($v)
    {
        $this->name = $v;
    }

    public function getName()
    {
        return $this->name;
    }

    /**
     * @return PearPkgMappingElement
     */
    public function createElement()
    {
        $e = new PearPkgMappingElement();
        $this->elements[] = $e;

        return $e;
    }

    /**
     * @return array
     */
    public function getElements()
    {
        return $this->elements;
    }

    /**
     * Returns the PHP hash or array of hashes (etc.) that this mapping represents.
     * @return array
     */
    public function getValue()
    {
        $value = array();
        foreach ($this->getElements() as $el) {
            if ($el->getKey() !== null) {
                $value[$el->getKey()] = $el->getValue();
            } else {
                $value[] = $el->getValue();
            }
        }

        return $value;
    }
}

/**
 * Sub-element of <mapping>.
 *
 * @package  phing.tasks.ext
 */
class PearPkgMappingElement
{

    private $key;
    private $value;
    private $elements = array();

    /**
     * @param $v
     */
    public function setKey($v)
    {
        $this->key = $v;
    }

    public function getKey()
    {
        return $this->key;
    }

    /**
     * @param $v
     */
    public function setValue($v)
    {
        $this->value = $v;
    }

    /**
     * Returns either the simple value or
     * the calculated value (array) of nested elements.
     * @return mixed
     */
    public function getValue()
    {
        if (!empty($this->elements)) {
            $value = array();
            foreach ($this->elements as $el) {
                if ($el->getKey() !== null) {
                    $value[$el->getKey()] = $el->getValue();
                } else {
                    $value[] = $el->getValue();
                }
            }

            return $value;
        } else {
            return $this->value;
        }
    }

    /**
     * Handles nested <element> tags.
     */
    public function createElement()
    {
        $e = new PearPkgMappingElement();
        $this->elements[] = $e;

        return $e;
    }

}

/**
 * Encapsulates file roles
 *
 * @package phing.tasks.ext
 */
class PearPkgRole
{
    /**
     * @var string
     */
    private $extension;

    /**
     * @var string
     */
    private $role;

    /**
     * Sets the file extension
     * @param string $extension
     */
    public function setExtension($extension)
    {
        $this->extension = $extension;
    }

    /**
     * Retrieves the file extension
     * @return string
     */
    public function getExtension()
    {
        return $this->extension;
    }

    /**
     * Sets the role
     * @param string $role
     */
    public function setRole($role)
    {
        $this->role = $role;
    }

    /**
     * Retrieves the role
     * @return string
     */
    public function getRole()
    {
        return $this->role;
    }
}
<?php
/*
 * $Id: 83edf075fa049a08540518b97ecb5f15d24c9c12 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/MatchingTask.php';
require_once 'phing/types/IterableFileSet.php';

/**
 * Data task for {@link http://php.net/manual/en/class.phardata.php PharData class}.
 *
 * @package phing.tasks.ext
 * @author Siad Ardroumli <siad.ardroumli@gmail.com>
 */
class PharDataTask extends MatchingTask
{
    /**
     * @var PhingFile
     */
    private $destinationFile;

    /**
     * @var int
     */
    private $compression = Phar::NONE;

    /**
     * Base directory, from where local package paths will be calculated.
     *
     * @var PhingFile
     */
    private $baseDirectory;

    /**
     * @var IterableFileSet[]
     */
    private $filesets = array();

    /**
     * @return FileSet
     */
    public function createFileSet()
    {
        $this->fileset = new IterableFileSet();
        $this->filesets[] = $this->fileset;
        return $this->fileset;
    }

    /**
     * Compression type (gzip, bzip2, none) to apply to the packed files.
     *
     * @param string $compression
     */
    public function setCompression($compression)
    {
        /**
         * If we don't support passed compression, leave old one.
         */
        switch ($compression) {
            case 'gzip':
                $this->compression = Phar::GZ;
                break;
            case 'bzip2':
                $this->compression = Phar::BZ2;
                break;
            default:
                break;
        }
    }

    /**
     * Destination (output) file.
     *
     * @param PhingFile $destinationFile
     */
    public function setDestFile(PhingFile $destinationFile)
    {
        $this->destinationFile = $destinationFile;
    }

    /**
     * Base directory, which will be deleted from each included file (from path).
     * Paths with deleted basedir part are local paths in archive.
     *
     * @param PhingFile $baseDirectory
     */
    public function setBaseDir(PhingFile $baseDirectory)
    {
        $this->baseDirectory = $baseDirectory;
    }

    /**
     * @throws BuildException
     */
    public function main()
    {
        $this->checkPreconditions();

        try {
            $this->log(
                'Building archive: ' . $this->destinationFile->__toString(),
                Project::MSG_INFO
            );

            /**
             * Delete old archive, if exists.
             */
            if ($this->destinationFile->exists()) {
                $isDeleted = $this->destinationFile->delete();
                if (!$isDeleted) {
                    $this->log("Could not delete destination file $this->destinationFile", Project::MSG_WARN);
                }
            }

            $pharData = new PharData($this->baseDirectory->getPath() . '/' . $this->destinationFile->getName());

            foreach ($this->filesets as $fileset) {
                $this->log(
                    'Adding specified files in ' . $fileset->getDir($this->project) . ' to archive',
                    Project::MSG_VERBOSE
                );

                $pharData->buildFromIterator($fileset->getIterator(), $fileset->getDir($this->project));
            }

            if ($this->compression !== PHAR::NONE && $pharData->canCompress($this->compression)) {
                try {
                    $pharData->compress($this->compression);
                } catch(UnexpectedValueException $uve) {
                    $pharData->compressFiles($this->compression);
                }

                unset($pharData);
            }
        } catch (Exception $e) {
            throw new BuildException(
                'Problem creating archive: ' . $e->getMessage(),
                $e,
                $this->getLocation()
            );
        }
    }

    /**
     * @throws BuildException
     */
    private function checkPreconditions()
    {
        if (!extension_loaded('phar')) {
            throw new BuildException(
                "PharDataTask require either PHP 5.3 or better or the PECL's Phar extension"
            );
        }

        if (is_null($this->destinationFile)) {
            throw new BuildException("destfile attribute must be set!", $this->getLocation());
        }

        if ($this->destinationFile->exists() && $this->destinationFile->isDirectory()) {
            throw new BuildException("destfile is a directory!", $this->getLocation());
        }

        if (!$this->destinationFile->canWrite()) {
            throw new BuildException("Can not write to the specified destfile!", $this->getLocation());
        }

        if (is_null($this->baseDirectory)) {
            throw new BuildException("basedir cattribute must be set", $this->getLocation());
        }

        if (!$this->baseDirectory->exists()) {
            throw new BuildException("basedir '" . (string) $this->baseDirectory . "' does not exist!",
                $this->getLocation());
        }
    }
}
<?php
/*
 * $Id: a2ac87525ca9022012d9961946b556eb0d3ba3f3 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/phar/PharMetadataElement.php';

/**
 * @package phing.tasks.ext.phar
 * @author Alexey Shockov <alexey@shockov.com>
 * @since 2.4.0
 */
class PharMetadata
{
    /**
     * @var array
     */
    protected $elements = array();

    /**
     * @return PharMetadataElement
     */
    public function createElement()
    {
        return ($this->elements[] = new PharMetadataElement());
    }

    /**
     * @return array
     */
    public function toArray()
    {
        $metadata = array();

        foreach ($this->elements as $element) {
            $metadata[$element->getName()] = $element->toArray();
        }

        return $metadata;
    }
}
<?php
/*
 * $Id: 9ca9c4f01d1363fedfc9fd016e0041377f47ff66 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/phar/PharMetadata.php';

/**
 * @package phing.tasks.ext.phar
 * @author Alexey Shockov <alexey@shockov.com>
 * @since 2.4.0
 */
class PharMetadataElement
    extends PharMetadata
{
    /**
     * @var string
     */
    private $name;
    /**
     * @var string
     */
    private $value;

    /**
     * @param string $value
     */
    public function setValue($value)
    {
        $this->value = $value;
    }

    /**
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Return array of
     *
     * @return string|array
     */
    public function getValue()
    {
        /*
         * Elements first!
         */

        return (empty($this->elements) ? $this->value : $this->elements);
    }

    /**
     * @return string|array
     */
    public function toArray()
    {
        return (empty($this->elements) ? $this->value : parent::toArray());
    }
}
<?php
/*
 * $Id: 16adcda5de07d6005fa96cbf7f0f4c713e03a1b3 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/MatchingTask.php';
require_once 'phing/types/IterableFileSet.php';
require_once 'phing/tasks/ext/phar/PharMetadata.php';

/**
 * Package task for {@link http://www.php.net/manual/en/book.phar.php Phar technology}.
 *
 * @package phing.tasks.ext
 * @author Alexey Shockov <alexey@shockov.com>
 * @since 2.4.0
 */
class PharPackageTask
    extends MatchingTask
{
    /**
     * @var PhingFile
     */
    private $destinationFile;

    /**
     * @var int
     */
    private $compression = Phar::NONE;

    /**
     * Base directory, from where local package paths will be calculated.
     *
     * @var PhingFile
     */
    private $baseDirectory;

    /**
     * @var PhingFile
     */
    private $cliStubFile;

    /**
     * @var PhingFile
     */
    private $webStubFile;

    /**
     * @var string
     */
    private $stubPath;

    /**
     * Private key the Phar will be signed with.
     *
     * @var PhingFile
     */
    private $key;

    /**
     * Password for the private key.
     *
     * @var string
     */
    private $keyPassword = '';

    /**
     * @var int
     */
    private $signatureAlgorithm = Phar::SHA1;

    /**
     * @var array
     */
    private $filesets = array();

    /**
     * @var PharMetadata
     */
    private $metadata = null;

    /**
     * @var string
     */
    private $alias;

    /**
     * @return PharMetadata
     */
    public function createMetadata()
    {
        return ($this->metadata = new PharMetadata());
    }

    /**
     * @return FileSet
     */
    public function createFileSet()
    {
        $this->fileset = new IterableFileSet();
        $this->filesets[] = $this->fileset;

        return $this->fileset;
    }

    /**
     * Signature algorithm (md5, sha1, sha256, sha512, openssl),
     * used for this package.
     *
     * @param string $algorithm
     */
    public function setSignature($algorithm)
    {
        /*
         * If we don't support passed algprithm, leave old one.
         */
        switch ($algorithm) {
            case 'md5':
                $this->signatureAlgorithm = Phar::MD5;
                break;
            case 'sha1':
                $this->signatureAlgorithm = Phar::SHA1;
                break;
            case 'sha256':
                $this->signatureAlgorithm = Phar::SHA256;
                break;
            case 'sha512':
                $this->signatureAlgorithm = Phar::SHA512;
                break;
            case 'openssl':
                $this->signatureAlgorithm = Phar::OPENSSL;
                break;
            default:
                break;
        }
    }

    /**
     * Compression type (gzip, bzip2, none) to apply to the packed files.
     *
     * @param string $compression
     */
    public function setCompression($compression)
    {
        /**
         * If we don't support passed compression, leave old one.
         */
        switch ($compression) {
            case 'gzip':
                $this->compression = Phar::GZ;
                break;
            case 'bzip2':
                $this->compression = Phar::BZ2;
                break;
            default:
                break;
        }
    }

    /**
     * Destination (output) file.
     *
     * @param PhingFile $destinationFile
     */
    public function setDestFile(PhingFile $destinationFile)
    {
        $this->destinationFile = $destinationFile;
    }

    /**
     * Base directory, which will be deleted from each included file (from path).
     * Paths with deleted basedir part are local paths in package.
     *
     * @param PhingFile $baseDirectory
     */
    public function setBaseDir(PhingFile $baseDirectory)
    {
        $this->baseDirectory = $baseDirectory;
    }

    /**
     * Relative path within the phar package to run,
     * if accessed on the command line.
     *
     * @param PhingFile $stubFile
     */
    public function setCliStub(PhingFile $stubFile)
    {
        $this->cliStubFile = $stubFile;
    }

    /**
     * Relative path within the phar package to run,
     * if accessed through a web browser.
     *
     * @param PhingFile $stubFile
     */
    public function setWebStub(PhingFile $stubFile)
    {
        $this->webStubFile = $stubFile;
    }

    /**
     * A path to a php file that contains a custom stub.
     *
     * @param string $stubPath
     */
    public function setStub($stubPath)
    {
        $this->stubPath = $stubPath;
    }

    /**
     * An alias to assign to the phar package.
     *
     * @param string $alias
     */
    public function setAlias($alias)
    {
        $this->alias = $alias;
    }

    /**
     * Sets the private key to use to sign the Phar with.
     *
     * @param PhingFile $key Private key to sign the Phar with.
     */
    public function setKey(PhingFile $key)
    {
        $this->key = $key;
    }

    /**
     * Password for the private key.
     *
     * @param string $keyPassword
     */
    public function setKeyPassword($keyPassword)
    {
        $this->keyPassword = $keyPassword;
    }

    /**
     * @throws BuildException
     */
    public function main()
    {
        $this->checkPreconditions();

        try {
            $this->log(
                'Building package: ' . $this->destinationFile->__toString(),
                Project::MSG_INFO
            );

            /**
             * Delete old package, if exists.
             */
            if ($this->destinationFile->exists()) {
                /**
                 * TODO Check operation for errors...
                 */
                $this->destinationFile->delete();
            }

            $phar = $this->buildPhar();
            $phar->startBuffering();

            $baseDirectory = realpath($this->baseDirectory->getPath());

            foreach ($this->filesets as $fileset) {
                $this->log(
                    'Adding specified files in ' . $fileset->getDir($this->project) . ' to package',
                    Project::MSG_VERBOSE
                );

                $phar->buildFromIterator($fileset, $baseDirectory);
            }

            $phar->stopBuffering();

            /**
             * File compression, if needed.
             */
            if (Phar::NONE != $this->compression) {
                $phar->compressFiles($this->compression);
            }

            if ($this->signatureAlgorithm == Phar::OPENSSL) {

                // Load up the contents of the key
                $keyContents = file_get_contents($this->key);

                // Attempt to load the given key as a PKCS#12 Cert Store first.
                if (openssl_pkcs12_read($keyContents, $certs, $this->keyPassword)) {
                    $private = openssl_pkey_get_private($certs['pkey']);
                } else {
                    // Fall back to a regular PEM-encoded private key.
                    // Setup an OpenSSL resource using the private key
                    // and tell the Phar to sign it using that key.
                    $private = openssl_pkey_get_private($keyContents, $this->keyPassword);
                }

                openssl_pkey_export($private, $pkey);
                $phar->setSignatureAlgorithm(Phar::OPENSSL, $pkey);

                // Get the details so we can get the public key and write that out
                // alongside the phar.
                $details = openssl_pkey_get_details($private);
                file_put_contents($this->destinationFile . '.pubkey', $details['key']);

            } else {
                $phar->setSignatureAlgorithm($this->signatureAlgorithm);
            }
        } catch (Exception $e) {
            throw new BuildException(
                'Problem creating package: ' . $e->getMessage(),
                $e,
                $this->getLocation()
            );
        }
    }

    /**
     * @throws BuildException
     */
    private function checkPreconditions()
    {
        if (!extension_loaded('phar')) {
            throw new BuildException(
                "PharPackageTask require either PHP 5.3 or better or the PECL's Phar extension"
            );
        }

        if (is_null($this->destinationFile)) {
            throw new BuildException("destfile attribute must be set!", $this->getLocation());
        }

        if ($this->destinationFile->exists() && $this->destinationFile->isDirectory()) {
            throw new BuildException("destfile is a directory!", $this->getLocation());
        }

        if (!$this->destinationFile->canWrite()) {
            throw new BuildException("Can not write to the specified destfile!", $this->getLocation());
        }
        if (!is_null($this->baseDirectory)) {
            if (!$this->baseDirectory->exists()) {
                throw new BuildException("basedir '" . (string) $this->baseDirectory . "' does not exist!", $this->getLocation(
                    ));
            }
        }
        if ($this->signatureAlgorithm == Phar::OPENSSL) {

            if (!extension_loaded('openssl')) {
                throw new BuildException("PHP OpenSSL extension is required for OpenSSL signing of Phars!", $this->getLocation(
                ));
            }

            if (is_null($this->key)) {
                throw new BuildException("key attribute must be set for OpenSSL signing!", $this->getLocation());
            }

            if (!$this->key->exists()) {
                throw new BuildException("key '" . (string) $this->key . "' does not exist!", $this->getLocation());
            }

            if (!$this->key->canRead()) {
                throw new BuildException("key '" . (string) $this->key . "' cannot be read!", $this->getLocation());
            }
        }
    }

    /**
     * Build and configure Phar object.
     *
     * @return Phar
     */
    private function buildPhar()
    {
        $phar = new Phar($this->destinationFile);

        if (!empty($this->stubPath)) {
            $phar->setStub(file_get_contents($this->stubPath));
        } else {
            if (!empty($this->cliStubFile)) {
                $cliStubFile = $this->cliStubFile->getPathWithoutBase($this->baseDirectory);
            } else {
                $cliStubFile = null;
            }

            if (!empty($this->webStubFile)) {
                $webStubFile = $this->webStubFile->getPathWithoutBase($this->baseDirectory);
            } else {
                $webStubFile = null;
            }

            $phar->setDefaultStub($cliStubFile, $webStubFile);
        }

        if ($this->metadata === null) {
            $this->createMetaData();
        }

        if ($metadata = $this->metadata->toArray()) {
            $phar->setMetadata($metadata);
        }

        if (!empty($this->alias)) {
            $phar->setAlias($this->alias);
        }

        return $phar;
    }
}
<?php
/**
 * $Id: 70e48a7d52be99d566ed1d9ad86d1dc5bd9add70 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/phk/PhkPackageWebAccess.php';

/**
 * See {@link http://phk.tekwire.net/} for more information about PHK.
 *
 * @author Alexey Shockov <alexey@shockov.com>
 * @package phing.tasks.ext.phk
 */
class PhkPackageTask extends Task
{
    /**
     * @var string
     */
    private $outputFile;
    /**
     * @var string
     */
    private $inputDirectory;
    /**
     * @var string
     */
    private $phkCreatorPath;
    /**
     * @var PhkPackageWebAccess
     */
    private $webAccess;
    /**
     * @var array
     */
    private $modifiers = array();
    /**
     * @var array
     */
    private $options = array();

    /**
     * @return PhkPackageWebAccess
     */
    public function createWebAccess()
    {
        return ($this->webAccess = new PhkPackageWebAccess());
    }

    /**
     * @param string $crcCheck
     */
    public function setCrcCheck($crcCheck)
    {
        $this->options['crc_check'] = ('true' == $crcCheck ? true : false);
    }

    /**
     * @param string $webRunScript
     */
    public function setWebRunScript($webRunScript)
    {
        $this->options['web_run_script'] = $webRunScript;
    }

    /**
     * @param string $cliRunScript
     */
    public function setCliRunScript($cliRunScript)
    {
        $this->options['cli_run_script'] = $cliRunScript;
    }

    /**
     * @param string $libRunScript
     */
    public function setLibRunScript($libRunScript)
    {
        $this->options['lib_run_script'] = $libRunScript;
    }

    /**
     * @param string $name
     */
    public function setName($name)
    {
        $this->options['name'] = $name;
    }

    /**
     * @param string $webMainRedirect
     */
    public function setWebMainRedirect($webMainRedirect)
    {
        $this->options['web_main_redirect'] = ('true' == $webMainRedirect ? true : false);
    }

    /**
     * @param string $pluginClass
     */
    public function setPluginClass($pluginClass)
    {
        $this->options['plugin_class'] = $pluginClass;
    }

    /**
     * @param string $version
     */
    public function setVersion($version)
    {
        $this->options['version'] = $version;
    }

    /**
     * @param string $summary
     */
    public function setSummary($summary)
    {
        $this->options['summary'] = $summary;
    }

    /**
     * @param string $inputDirectory
     */
    public function setInputDirectory($inputDirectory)
    {
        $this->inputDirectory = $inputDirectory;
    }

    /**
     * @param string $outputFile
     */
    public function setOutputFile($outputFile)
    {
        $this->outputFile = $outputFile;
    }

    /**
     * May be none, gzip or bzip2.
     *
     * @param string $compress
     */
    public function setCompress($compress)
    {
        $this->modifiers['compress'] = $compress;
    }

    /**
     * True or false.
     *
     * @param srting $strip
     */
    public function setStrip($strip)
    {
        $this->modifiers['strip'] = $strip;
    }

    /**
     * Path to PHK_Creator.phk file.
     *
     * @param srting $path
     */
    public function setPhkCreatorPath($path)
    {
        $this->phkCreatorPath = $path;
    }

    /**
     *
     */
    public function init()
    {

    }

    /**
     * Main method...
     */
    public function main()
    {
        /*
         * Check for empty first - speed ;)
         */
        if (!is_file($this->phkCreatorPath)) {
            throw new BuildException('You must specify the "phkcreatorpath" attribute for PHK task.');
        }
        if (empty($this->inputDirectory)) {
            throw new BuildException('You must specify the "inputdirectory" attribute for PHK task.');
        }
        if (empty($this->outputFile)) {
            throw new BuildException('You must specify the "outputfile" attribute for PHK task.');
        }

        require_once $this->phkCreatorPath;

        $mountPoint = PHK_Mgr::mount($this->outputFile, PHK::F_CREATOR);
        $phkManager = PHK_Mgr::instance($mountPoint);

        /*
         * Add files.
         */
        $phkManager->ftree()->merge_file_tree('/', $this->inputDirectory, $this->modifiers);

        /*
         * Add web_access to options, if present.
         */
        if (!is_null($this->webAccess)) {
            $webAccessPaths = $this->webAccess->getPaths();
            if (!empty($webAccessPaths)) {
                $this->options['web_access'] = $webAccessPaths;
            }
        }

        $phkManager->set_options($this->options);

        /*
         * Intercept output (in PHP we can't intercept stream).
         */
        ob_start();
        /*
         * Create file...
         */
        $phkManager->dump();
        /*
         * Print with Phing log...
         */
        $output = trim(ob_get_clean());
        $output = explode("\n", $output);
        foreach ($output as $line) {
            /*
             * Delete all '--- *' lines. Bluh!
             */
            /*
             * TODO Change preg_math to more faster alternative.
             */
            if (preg_match('/^---/', $line)) {
                continue;
            }

            $this->log($line);
        }

        /*
         * Set rights for generated file... Don't use umask() - see
         * notes in official documentation for this function.
         */
        chmod($this->outputFile, 0644);
    }
}
<?php
/**
 * $Id: 37ba9ef129372cad5ee8e00607f98042ec1c59fe $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/phk/PhkPackageWebAccessPath.php';

/**
 * @author Alexey Shockov <alexey@shockov.com>
 * @package phing.tasks.ext.phk
 */
class PhkPackageWebAccess
{
    /**
     * @var array
     */
    private $paths = array();

    /**
     * @return PhkPackageWebAccessPath
     */
    public function createPath()
    {
        return ($this->paths[] = new PhkPackageWebAccessPath());
    }

    /**
     * @return array
     */
    public function getPaths()
    {
        /*
         * Get real paths...
         */
        $paths = array();

        foreach ($this->paths as $path) {
            $paths[] = $path->getPath();
        }

        return $paths;
    }
}
<?php
/**
 * $Id: 9f3afed1a42d813cbd8fdb0c86baa2265e7e820c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * @author Alexey Shockov <alexey@shockov.com>
 * @package phing.tasks.ext.phk
 */
class PhkPackageWebAccessPath
{
    /**
     * @var string
     */
    private $path;

    /**
     * @param string $path
     */
    public function addText($path)
    {
        $this->path = trim($path);
    }

    /**
     * @return string
     */
    public function getPath()
    {
        return $this->path;
    }
}
<?php
/*
 *  $Id: 1cb309b7992d03271825878803e5cabb757b9d63 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * A PHP code sniffer task. Checking the style of one or more PHP source files.
 *
 * @author  Dirk Thomas <dirk.thomas@4wdmedia.de>
 * @version $Id: 1cb309b7992d03271825878803e5cabb757b9d63 $
 * @package phing.tasks.ext
 */
class PhpCodeSnifferTask extends Task
{

    /**
     * A php source code filename or directory
     *
     * @var PhingFile
     */
    protected $file; // the source file (from xml attribute)

    /**
     * All fileset objects assigned to this task
     *
     * @var FileSet[]
     */
    protected $filesets = array(); // all fileset objects assigned to this task

    // parameters for php code sniffer
    protected $standards = array('Generic');
    protected $sniffs = array();
    protected $showWarnings = true;
    protected $showSources = false;
    protected $reportWidth = 80;
    protected $verbosity = 0;
    protected $tabWidth = 0;
    protected $allowedFileExtensions = array('php', 'inc', 'js', 'css');
    protected $allowedTypes = array();
    protected $ignorePatterns = false;
    protected $noSubdirectories = false;
    protected $configData = array();
    protected $encoding = 'iso-8859-1';

    // parameters to customize output
    protected $showSniffs = false;
    protected $format = 'full';

    /**
     * @var PhpCodeSnifferTask_FormatterElement[]
     */
    protected $formatters = array();

    /**
     * Holds the type of the doc generator
     *
     * @var string
     */
    protected $docGenerator = '';

    /**
     * Holds the outfile for the documentation
     *
     * @var PhingFile
     */
    protected $docFile = null;

    private $haltonerror = false;
    private $haltonwarning = false;
    private $skipversioncheck = false;
    private $propertyName = null;

    /**
     * Cache data storage
     * @var DataStore
     */
    protected $cache;

    /**
     * Load the necessary environment for running PHP_CodeSniffer.
     *
     * @return void
     */
    public function init()
    {
    }

    /**
     * File to be performed syntax check on
     * @param PhingFile $file
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Sets the coding standard to test for
     *
     * @param string $standards The coding standards
     *
     * @return void
     */
    public function setStandard($standards)
    {
        $this->standards = array();
        $token = ' ,;';
        $ext = strtok($standards, $token);
        while ($ext !== false) {
            $this->standards[] = $ext;
            $ext = strtok($token);
        }
    }

    /**
     * Sets the sniffs which the standard should be restricted to
     * @param string $sniffs
     */
    public function setSniffs($sniffs)
    {
        $token = ' ,;';
        $sniff = strtok($sniffs, $token);
        while ($sniff !== false) {
            $this->sniffs[] = $sniff;
            $sniff = strtok($token);
        }
    }

    /**
     * Sets the type of the doc generator
     *
     * @param string $generator HTML or Text
     *
     * @return void
     */
    public function setDocGenerator($generator)
    {
        $this->docGenerator = $generator;
    }

    /**
     * Sets the outfile for the documentation
     *
     * @param PhingFile $file The outfile for the doc
     *
     * @return void
     */
    public function setDocFile(PhingFile $file)
    {
        $this->docFile = $file;
    }

    /**
     * Sets the flag if warnings should be shown
     * @param boolean $show
     */
    public function setShowWarnings($show)
    {
        $this->showWarnings = StringHelper::booleanValue($show);
    }

    /**
     * Sets the flag if sources should be shown
     *
     * @param boolean $show Whether to show sources or not
     *
     * @return void
     */
    public function setShowSources($show)
    {
        $this->showSources = StringHelper::booleanValue($show);
    }

    /**
     * Sets the width of the report
     *
     * @param int $width How wide the screen reports should be.
     *
     * @return void
     */
    public function setReportWidth($width)
    {
        $this->reportWidth = (int) $width;
    }

    /**
     * Sets the verbosity level
     * @param int $level
     */
    public function setVerbosity($level)
    {
        $this->verbosity = (int) $level;
    }

    /**
     * Sets the tab width to replace tabs with spaces
     * @param int $width
     */
    public function setTabWidth($width)
    {
        $this->tabWidth = (int) $width;
    }

    /**
     * Sets file encoding
     * @param string $encoding
     */
    public function setEncoding($encoding)
    {
        $this->encoding = $encoding;
    }

    /**
     * Sets the allowed file extensions when using directories instead of specific files
     * @param array $extensions
     */
    public function setAllowedFileExtensions($extensions)
    {
        $this->allowedFileExtensions = array();
        $token = ' ,;';
        $ext = strtok($extensions, $token);
        while ($ext !== false) {
            $this->allowedFileExtensions[] = $ext;
            $ext = strtok($token);
        }
    }

    /**
     * Sets the allowed types for the PHP_CodeSniffer::suggestType()
     * @param array $types
     */
    public function setAllowedTypes($types)
    {
        $this->allowedTypes = array();
        $token = ' ,;';
        $type = strtok($types, $token);
        while ($type !== false) {
            $this->allowedTypes[] = $type;
            $type = strtok($token);
        }
    }

    /**
     * Sets the ignore patterns to skip files when using directories instead of specific files
     * @param $patterns
     * @internal param array $extensions
     */
    public function setIgnorePatterns($patterns)
    {
        $this->ignorePatterns = array();
        $token = ' ,;';
        $pattern = strtok($patterns, $token);
        while ($pattern !== false) {
            $this->ignorePatterns[$pattern] = 'relative';
            $pattern = strtok($token);
        }
    }

    /**
     * Sets the flag if subdirectories should be skipped
     * @param boolean $subdirectories
     */
    public function setNoSubdirectories($subdirectories)
    {
        $this->noSubdirectories = StringHelper::booleanValue($subdirectories);
    }

    /**
     * Creates a config parameter for this task
     *
     * @return Parameter The created parameter
     */
    public function createConfig()
    {
        $num = array_push($this->configData, new Parameter());

        return $this->configData[$num - 1];
    }

    /**
     * Sets the flag if the used sniffs should be listed
     * @param boolean $show
     */
    public function setShowSniffs($show)
    {
        $this->showSniffs = StringHelper::booleanValue($show);
    }

    /**
     * Sets the output format
     * @param string $format
     */
    public function setFormat($format)
    {
        $this->format = $format;
    }

    /**
     * Create object for nested formatter element.
     * @return PhpCodeSnifferTask_FormatterElement
     */
    public function createFormatter()
    {
        $num = array_push(
            $this->formatters,
            new PhpCodeSnifferTask_FormatterElement()
        );

        return $this->formatters[$num - 1];
    }

    /**
     * Sets the haltonerror flag
     * @param boolean $value
     */
    public function setHaltonerror($value)
    {
        $this->haltonerror = $value;
    }

    /**
     * Sets the haltonwarning flag
     * @param boolean $value
     */
    public function setHaltonwarning($value)
    {
        $this->haltonwarning = $value;
    }

    /**
     * Sets the skipversioncheck flag
     * @param boolean $value
     */
    public function setSkipVersionCheck($value)
    {
        $this->skipversioncheck = $value;
    }

    /**
     * Sets the name of the property to use
     * @param $propertyName
     */
    public function setPropertyName($propertyName)
    {
        $this->propertyName = $propertyName;
    }

    /**
     * Returns the name of the property to use
     */
    public function getPropertyName()
    {
        return $this->propertyName;
    }

    /**
     * Whether to store last-modified times in cache
     *
     * @param PhingFile $file
     */
    public function setCacheFile(PhingFile $file)
    {
        $this->cache = new DataStore($file);
    }

    /**
     * Return the list of files to parse
     *
     * @return string[] list of absolute files to parse
     */
    protected function getFilesToParse()
    {
        $filesToParse = array();

        if ($this->file instanceof PhingFile) {
            $filesToParse[] = $this->file->getPath();
        } else {
            // append any files in filesets
            foreach ($this->filesets as $fs) {
                $dir = $fs->getDir($this->project)->getAbsolutePath();
                foreach ($fs->getDirectoryScanner($this->project)->getIncludedFiles() as $filename) {
                    $fileAbsolutePath = $dir . DIRECTORY_SEPARATOR . $filename;
                    if ($this->cache) {
                        $lastMTime = $this->cache->get($fileAbsolutePath);
                        $currentMTime = filemtime($fileAbsolutePath);
                        if ($lastMTime >= $currentMTime) {
                            continue;
                        } else {
                            $this->cache->put($fileAbsolutePath, $currentMTime);
                        }
                    }
                    $filesToParse[] = $fileAbsolutePath;
                }
            }
        }
        return $filesToParse;
    }

    /**
     * Executes PHP code sniffer against PhingFile or a FileSet
     */
    public function main()
    {
        if (!class_exists('PHP_CodeSniffer')) {
            @include_once 'PHP/CodeSniffer.php';

            if (!class_exists('PHP_CodeSniffer')) {
                throw new BuildException("This task requires the PHP_CodeSniffer package installed and available on the include path", $this->getLocation(
                ));
            }
        }

        /**
         * Determine PHP_CodeSniffer version number
         */
        if (!$this->skipversioncheck) {
            if (defined('PHP_CodeSniffer::VERSION')) {
                preg_match('/\d\.\d\.\d/', PHP_CodeSniffer::VERSION, $version);
            } else {
                preg_match('/\d\.\d\.\d/', shell_exec('phpcs --version'), $version);
            }

            if (version_compare($version[0], '1.2.2') < 0) {
                throw new BuildException(
                    'PhpCodeSnifferTask requires PHP_CodeSniffer version >= 1.2.2',
                    $this->getLocation()
                );
            }
        }

        if (!isset($this->file) and count($this->filesets) == 0) {
            throw new BuildException("Missing either a nested fileset or attribute 'file' set");
        }

        if (count($this->formatters) == 0) {
            // turn legacy format attribute into formatter
            $fmt = new PhpCodeSnifferTask_FormatterElement();
            $fmt->setType($this->format);
            $fmt->setUseFile(false);
            $this->formatters[] = $fmt;
        }

        $fileList = $this->getFilesToParse();

        $cwd = getcwd();

        // Save command line arguments because it confuses PHPCS (version 1.3.0)
        $oldArgs = $_SERVER['argv'];
        $_SERVER['argv'] = array();
        $_SERVER['argc'] = 0;

        include_once 'phing/tasks/ext/phpcs/PhpCodeSnifferTask_Wrapper.php';

        $codeSniffer = new PhpCodeSnifferTask_Wrapper($this->verbosity, $this->tabWidth, $this->encoding);
        $codeSniffer->setAllowedFileExtensions($this->allowedFileExtensions);
        if ($this->allowedTypes) {
            PhpCodeSnifferTask_Wrapper::$allowedTypes = $this->allowedTypes;
        }
        if (is_array($this->ignorePatterns)) {
            $codeSniffer->setIgnorePatterns($this->ignorePatterns);
        }
        foreach ($this->configData as $configData) {
            $codeSniffer->setConfigData($configData->getName(), $configData->getValue(), true);
        }

        /*
         * Verifying if standard is installed only after setting config data.
         * Custom standard paths could be provided via installed_paths config parameter.
         */
        foreach($this->standards as $standard) {
            if (PHP_CodeSniffer::isInstalledStandard($standard) === false) {
                // They didn't select a valid coding standard, so help them
                // out by letting them know which standards are installed.
                $installedStandards = PHP_CodeSniffer::getInstalledStandards();
                $numStandards = count($installedStandards);
                $errMsg = '';

                if ($numStandards === 0) {
                    $errMsg = 'No coding standards are installed.';
                } else {
                    $lastStandard = array_pop($installedStandards);

                    if ($numStandards === 1) {
                        $errMsg = 'The only coding standard installed is ' . $lastStandard;
                    } else {
                        $standardList = implode(', ', $installedStandards);
                        $standardList .= ' and ' . $lastStandard;
                        $errMsg = 'The installed coding standards are ' . $standardList;
                    }
                }

                throw new BuildException(
                    'ERROR: the "' . $standard . '" coding standard is not installed. ' . $errMsg,
                    $this->getLocation()
                );
            }
        }

        if (!$this->showWarnings) {
            $codeSniffer->cli->warningSeverity = 0;
        }

        // nasty integration hack
        $values = $codeSniffer->cli->getDefaults();
        $_SERVER['argv'] = array('t');
        $_SERVER['argc'] = 1;
        foreach ($this->formatters as $fe) {
            if ($fe->getUseFile()) {
                $_SERVER['argv'][] = '--report-' . $fe->getType() . '=' . $fe->getOutfile();
            } else {
                $_SERVER['argv'][] = '--report-' . $fe->getType();
            }

            $_SERVER['argc']++;
        }

        if ($this->cache) {
            require_once 'phing/tasks/ext/phpcs/Reports_PhingRemoveFromCache.php';
            PHP_CodeSniffer_Reports_PhingRemoveFromCache::setCache($this->cache);
            // add a fake report to remove from cache
            $_SERVER['argv'][] = '--report-phingRemoveFromCache=';
            $_SERVER['argc']++;
        }

        $codeSniffer->process($fileList, $this->standards, $this->sniffs, $this->noSubdirectories);
        $_SERVER['argv'] = array();
        $_SERVER['argc'] = 0;

        if ($this->cache) {
            PHP_CodeSniffer_Reports_PhingRemoveFromCache::setCache(null);
            $this->cache->commit();
        }

        $this->printErrorReport($codeSniffer);

        // generate the documentation
        if ($this->docGenerator !== '' && $this->docFile !== null) {
            ob_start();

            $codeSniffer->generateDocs($this->standards, $this->sniffs, $this->docGenerator);

            $output = ob_get_contents();
            ob_end_clean();

            // write to file
            $outputFile = $this->docFile->getPath();
            $check = file_put_contents($outputFile, $output);

            if ($check === false) {
                throw new BuildException('Error writing doc to ' . $outputFile);
            }
        } elseif ($this->docGenerator !== '' && $this->docFile === null) {
            $codeSniffer->generateDocs($this->standards, $this->sniffs, $this->docGenerator);
        }

        if ($this->haltonerror && $codeSniffer->reporting->totalErrors > 0) {
            throw new BuildException('phpcodesniffer detected ' . $codeSniffer->reporting->totalErrors . ' error' . ($codeSniffer->reporting->totalErrors > 1 ? 's' : ''));
        }

        if ($this->haltonwarning && $codeSniffer->reporting->totalWarnings > 0) {
            throw new BuildException('phpcodesniffer detected ' . $codeSniffer->reporting->totalWarnings . ' warning' . ($codeSniffer->reporting->totalWarnings > 1 ? 's' : ''));
        }

        $_SERVER['argv'] = $oldArgs;
        $_SERVER['argc'] = count($oldArgs);
        chdir($cwd);
    }

    /**
     * Prints the error report.
     *
     * @param PHP_CodeSniffer $phpcs The PHP_CodeSniffer object containing
     *                               the errors.
     */
    protected function printErrorReport($phpcs)
    {
        $sniffs = $phpcs->getSniffs();
        $sniffStr = '';
        foreach ($sniffs as $sniff) {
            if (is_string($sniff)) {
                $sniffStr .= '- ' . $sniff . PHP_EOL;
            } else {
                $sniffStr .= '- ' . get_class($sniff) . PHP_EOL;
            }
        }
        $this->project->setProperty($this->getPropertyName(), (string) $sniffStr);

        if ($this->showSniffs) {
            $this->log('The list of used sniffs (#' . count($sniffs) . '): ' . PHP_EOL . $sniffStr, Project::MSG_INFO);
        }

        // process output
        $reporting = $phpcs->reporting;
        foreach ($this->formatters as $fe) {
            $reportFile = null;

            if ($fe->getUseFile()) {
                $reportFile = $fe->getOutfile();
                //ob_start();
            }

            // Crude check, but they broke backwards compatibility
            // with a minor version release.
            if (PHP_CodeSniffer::VERSION >= '2.2.0') {
                $cliValues = array('colors' => false);
                $reporting->printReport($fe->getType(),
                                        $this->showSources,
                                        $cliValues,
                                        $reportFile,
                                        $this->reportWidth);
            } else {
                $reporting->printReport($fe->getType(),
                                        $this->showSources,
                                        $reportFile,
                                        $this->reportWidth);
            }

            // reporting class uses ob_end_flush(), but we don't want
            // an output if we use a file
            if ($fe->getUseFile()) {
                //ob_end_clean();
            }
        }
    }

    /**
     * Outputs the results with a custom format
     *
     * @param array $report Packaged list of all errors in each file
     */
    protected function outputCustomFormat($report)
    {
        $files = $report['files'];
        foreach ($files as $file => $attributes) {
            $errors = $attributes['errors'];
            $warnings = $attributes['warnings'];
            $messages = $attributes['messages'];
            if ($errors > 0) {
                $this->log(
                    $file . ': ' . $errors . ' error' . ($errors > 1 ? 's' : '') . ' detected',
                    Project::MSG_ERR
                );
                $this->outputCustomFormatMessages($messages, 'ERROR');
            } else {
                $this->log($file . ': No syntax errors detected', Project::MSG_VERBOSE);
            }
            if ($warnings > 0) {
                $this->log(
                    $file . ': ' . $warnings . ' warning' . ($warnings > 1 ? 's' : '') . ' detected',
                    Project::MSG_WARN
                );
                $this->outputCustomFormatMessages($messages, 'WARNING');
            }
        }

        $totalErrors = $report['totals']['errors'];
        $totalWarnings = $report['totals']['warnings'];
        $this->log(count($files) . ' files were checked', Project::MSG_INFO);
        if ($totalErrors > 0) {
            $this->log($totalErrors . ' error' . ($totalErrors > 1 ? 's' : '') . ' detected', Project::MSG_ERR);
        } else {
            $this->log('No syntax errors detected', Project::MSG_INFO);
        }
        if ($totalWarnings > 0) {
            $this->log($totalWarnings . ' warning' . ($totalWarnings > 1 ? 's' : '') . ' detected', Project::MSG_INFO);
        }
    }

    /**
     * Outputs the messages of a specific type for one file
     * @param array  $messages
     * @param string $type
     */
    protected function outputCustomFormatMessages($messages, $type)
    {
        foreach ($messages as $line => $messagesPerLine) {
            foreach ($messagesPerLine as $column => $messagesPerColumn) {
                foreach ($messagesPerColumn as $message) {
                    $msgType = $message['type'];
                    if ($type == $msgType) {
                        $logLevel = Project::MSG_INFO;
                        if ($msgType == 'ERROR') {
                            $logLevel = Project::MSG_ERR;
                        } else {
                            if ($msgType == 'WARNING') {
                                $logLevel = Project::MSG_WARN;
                            }
                        }
                        $text = $message['message'];
                        $string = $msgType . ' in line ' . $line . ' column ' . $column . ': ' . $text;
                        $this->log($string, $logLevel);
                    }
                }
            }
        }
    }

} //end phpCodeSnifferTask

/**
 * @package phing.tasks.ext
 */
class PhpCodeSnifferTask_FormatterElement extends DataType
{

    /**
     * Type of output to generate
     * @var string
     */
    protected $type = "";

    /**
     * Output to file?
     * @var bool
     */
    protected $useFile = true;

    /**
     * Output file.
     * @var string
     */
    protected $outfile = "";

    /**
     * Validate config.
     */
    public function parsingComplete()
    {
        if (empty($this->type)) {
            throw new BuildException("Format missing required 'type' attribute.");
        }
        if ($this->useFile && empty($this->outfile)) {
            throw new BuildException("Format requires 'outfile' attribute when 'useFile' is true.");
        }

    }

    /**
     * @param $type
     */
    public function setType($type)
    {
        $this->type = $type;
    }

    /**
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * @param $useFile
     */
    public function setUseFile($useFile)
    {
        $this->useFile = $useFile;
    }

    /**
     * @return bool
     */
    public function getUseFile()
    {
        return $this->useFile;
    }

    /**
     * @param $outfile
     */
    public function setOutfile($outfile)
    {
        $this->outfile = $outfile;
    }

    /**
     * @return string
     */
    public function getOutfile()
    {
        return $this->outfile;
    }

} //end FormatterElement
<?php
/**
 * $Id: 20db9b04d2f5ea6acc7db0f00d83daa6b8580cec $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/phpcpd/formatter/PHPCPDResultFormatter.php';

/**
 * Prints plain text output of phpcpd run
 *
 * @package phing.tasks.ext.phpcpd.formatter
 * @author  Benjamin Schultz <bschultz@proqrent.de>
 * @version $Id: 20db9b04d2f5ea6acc7db0f00d83daa6b8580cec $
 */
class DefaultPHPCPDResultFormatter extends PHPCPDResultFormatter
{
    /**
     * Processes a list of clones.
     *
     * @param CodeCloneMap   $clones
     * @param Project        $project
     * @param boolean        $useFile
     * @param PhingFile|null $outFile
     */
    public function processClones($clones, Project $project, $useFile = false, $outFile = null)
    {
        if (get_class($clones) == 'SebastianBergmann\PHPCPD\CodeCloneMap') {
            if (class_exists('SebastianBergmann\PHPCPD\Log\Text')) {
                $this->processClonesNew($clones, $useFile, $outFile);

                return;
            }

            $logger = new \SebastianBergmann\PHPCPD\TextUI\ResultPrinter();
        } else {
            $logger = new PHPCPD_TextUI_ResultPrinter();
        }

        // default format goes to logs, no buffering
        ob_start();
        $logger->printResult($clones, $project->getBaseDir(), true);
        $output = ob_get_contents();
        ob_end_clean();

        if (!$useFile || empty($outFile)) {
            echo $output;
        } else {
            file_put_contents($outFile->getPath(), $output);
        }
    }

    /**
     * Wrapper for PHPCPD 2.0
     *
     * @param CodeCloneMap   $clones
     * @param boolean        $useFile
     * @param PhingFile|null $outFile
     */
    private function processClonesNew($clones, $useFile = false, $outFile = null)
    {
        if ($useFile) {
            $resource = fopen($outFile->getPath(), "w");
        } else {
            $resource = fopen("php://output", "w");
        }

        $output = new \Symfony\Component\Console\Output\StreamOutput($resource);
        $logger = new \SebastianBergmann\PHPCPD\Log\Text();
        $logger->printResult($output, $clones);
    }
}
<?php
/**
 * $Id: e8921b5a15fd23ce437471250c7161884560e202 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * This abstract class describes classes that format the results of a PHPCPD run.
 *
 * @package phing.tasks.ext.phpcpd.formatter
 * @author  Benjamin Schultz <bschultz@proqrent.de>
 * @version $Id: e8921b5a15fd23ce437471250c7161884560e202 $
 */
abstract class PHPCPDResultFormatter
{
    /**
     * Processes a list of clones.
     *
     * @param object         $clones
     * @param Project        $project
     * @param boolean        $useFile
     * @param PhingFile|null $outFile
     */
    abstract public function processClones($clones, Project $project, $useFile = false, $outFile = null);
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/phpcpd/formatter/PHPCPDResultFormatter.php';

/**
 * Prints PMD-XML output of phpcpd run
 *
 * @author  Benjamin Schultz <bschultz@proqrent.de>
 *
 * @package phing.tasks.ext.phpcpd.formatter
 */
class PMDPHPCPDResultFormatter extends PHPCPDResultFormatter
{
    /**
     * Processes a list of clones.
     *
     * @param PHPCPD_CloneMap|CodeCloneMap $clones
     * @param Project                      $project
     * @param boolean                      $useFile
     * @param PhingFile|null               $outFile
     *
     * @throws BuildException
     */
    public function processClones($clones, Project $project, $useFile = false, $outFile = null)
    {
        if (!$useFile || empty($outFile)) {
            throw new BuildException('Output filename required for this formatter');
        }

        if (get_class($clones) == 'PHPCPD_CloneMap') {
            $logger = new PHPCPD_Log_XML_PMD($outFile);
        } else {
            $logger = new \SebastianBergmann\PHPCPD\Log\PMD($outFile);
        }

        $logger->processClones($clones);
    }
}
<?php
/**
 * $Id: c430c57cb2db4bff221d0bcf96ed4396b738da6d $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/PhingFile.php';

/**
 * A wrapper for the implementations of PHPCPDResultFormatter.
 *
 * @package phing.tasks.ext.phpcpd
 * @author  Benjamin Schultz <bschultz@proqrent.de>
 * @version $Id: c430c57cb2db4bff221d0bcf96ed4396b738da6d $
 */
class PHPCPDFormatterElement
{
    /**
     * The report result formatter.
     *
     * @var PHPCPDResultFormatter
     */
    protected $formatter = null;

    /**
     * The type of the formatter.
     *
     * @var string
     */
    protected $type = '';

    /**
     * Whether to use file (or write output to phing log).
     *
     * @var boolean
     */
    protected $useFile = true;

    /**
     * Output file for formatter.
     *
     * @var PhingFile
     */
    protected $outfile = null;

    /**
     * The parent task
     *
     * @var PHPCPDTask
     */
    private $parentTask;

    /**
     * Construct a new PHPCPDFormatterElement with parent task.
     *
     * @param PHPCPDTask $parentTask
     */
    public function __construct(PHPCPDTask $parentTask)
    {
        $this->parentTask = $parentTask;
    }

    /**
     * Sets the formatter type.
     *
     * @param string $type Type of the formatter
     *
     * @throws BuildException
     */
    public function setType($type)
    {
        $this->type = $type;

        switch ($this->type) {
            case 'pmd':
                if ($this->useFile === false) {
                    throw new BuildException('Formatter "' . $this->type . '" can only print the result to an file');
                }

                include_once 'phing/tasks/ext/phpcpd/formatter/PMDPHPCPDResultFormatter.php';

                $this->formatter = new PMDPHPCPDResultFormatter();
                break;

            case 'default':
                include_once 'phing/tasks/ext/phpcpd/formatter/DefaultPHPCPDResultFormatter.php';

                $this->formatter = new DefaultPHPCPDResultFormatter();
                break;

            default:
                throw new BuildException('Formatter "' . $this->type . '" not implemented');
        }
    }

    /**
     * Get the formatter type
     *
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Set whether to write formatter results to file or not.
     *
     * @param boolean $useFile True or false.
     */
    public function setUseFile($useFile)
    {
        $this->useFile = StringHelper::booleanValue($useFile);
    }

    /**
     * Return whether to write formatter results to file or not.
     *
     * @return boolean
     */
    public function getUseFile()
    {
        return $this->useFile;
    }

    /**
     * Sets the output file for the formatter results.
     *
     * @param PhingFile $outfile The output file
     */
    public function setOutfile(PhingFile $outfile)
    {
        $this->outfile = $outfile;
    }

    /**
     * Get the output file.
     *
     * @return PhingFile
     */
    public function getOutfile()
    {
        return $this->outfile;
    }

    /**
     * Returns the report formatter.
     *
     * @return PHPCPDResultFormatter
     */
    public function getFormatter()
    {
        return $this->formatter;
    }
}
<?php
/**
 *  $Id: 99ac61885f274b13a79c125b92a133b65db3b726 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/phpcpd/PHPCPDFormatterElement.php';

/**
 * Runs PHP Copy & Paste Detector. Checking PHP files for duplicated code.
 * Refactored original PhpCpdTask provided by
 * Timo Haberkern <timo.haberkern@fantastic-bits.de>
 *
 * @package phing.tasks.ext.phpcpd
 * @author  Benjamin Schultz <bschultz@proqrent.de>
 * @version $Id: 99ac61885f274b13a79c125b92a133b65db3b726 $
 */
class PHPCPDTask extends Task
{
    /**
     * A php source code filename or directory
     *
     * @var PhingFile
     */
    protected $file = null;

    /**
     * All fileset objects assigned to this task
     *
     * @var FileSet[]
     */
    protected $filesets = array();

    /**
     * Minimum number of identical lines.
     *
     * @var integer
     */
    protected $minLines = 5;

    /**
     * Minimum number of identical tokens.
     *
     * @var integer
     */
    protected $minTokens = 70;

    /**
     * Allow for fuzzy matches.
     *
     * @var boolean
     */
    protected $fuzzy = false;

    /**
     * List of valid file extensions for analyzed files.
     *
     * @var array
     */
    protected $allowedFileExtensions = array('php');

    /**
     * List of exclude directory patterns.
     *
     * @var array
     */
    protected $ignorePatterns = array('.git', '.svn', 'CVS', '.bzr', '.hg');

    /**
     * The format for the report
     *
     * @var string
     */
    protected $format = 'default';

    /**
     * Formatter elements.
     *
     * @var PHPCPDFormatterElement[]
     */
    protected $formatters = array();

    /**
     * @var bool
     */
    protected $oldVersion = false;

    /**
     * @var string
     */
    private $pharLocation = "";

    /**
     * Set the input source file or directory.
     *
     * @param PhingFile $file The input source file or directory.
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * Nested creator, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs List of files to scan
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Sets the minimum number of identical lines (default: 5).
     *
     * @param integer $minLines Minimum number of identical lines
     */
    public function setMinLines($minLines)
    {
        $this->minLines = $minLines;
    }

    /**
     * Sets the minimum number of identical tokens (default: 70).
     *
     * @param integer $minTokens Minimum number of identical tokens
     */
    public function setMinTokens($minTokens)
    {
        $this->minTokens = $minTokens;
    }

    /**
     * Sets the fuzzy match (default: false).
     *
     * @param boolean $fuzzy fuzzy match
     */
    public function setFuzzy($fuzzy)
    {
        $this->fuzzy = $fuzzy;
    }

    /**
     * Sets a list of filename extensions for valid php source code files.
     *
     * @param string $fileExtensions List of valid file extensions.
     */
    public function setAllowedFileExtensions($fileExtensions)
    {
        $this->allowedFileExtensions = array();

        $token = ' ,;';
        $ext = strtok($fileExtensions, $token);

        while ($ext !== false) {
            $this->allowedFileExtensions[] = $ext;
            $ext = strtok($token);
        }
    }

    /**
     * Sets a list of ignore patterns that is used to exclude directories from the source analysis.
     *
     * @param string $ignorePatterns List of ignore patterns.
     */
    public function setIgnorePatterns($ignorePatterns)
    {
        $this->ignorePatterns = array();

        $token = ' ,;';
        $pattern = strtok($ignorePatterns, $token);

        while ($pattern !== false) {
            $this->ignorePatterns[] = $pattern;
            $pattern = strtok($token);
        }
    }

    /**
     * Sets the output format
     *
     * @param string $format Format of the report
     */
    public function setFormat($format)
    {
        $this->format = $format;
    }

    /**
     * Create object for nested formatter element.
     *
     * @return PHPCPDFormatterElement
     */
    public function createFormatter()
    {
        $num = array_push($this->formatters, new PHPCPDFormatterElement($this));

        return $this->formatters[$num - 1];
    }

    /**
     * @param string $pharLocation
     */
    public function setPharLocation($pharLocation)
    {
        $this->pharLocation = $pharLocation;
    }

    /**
     * @throws BuildException if the phpcpd classes can't be loaded
     */
    private function loadDependencies()
    {
        if (!empty($this->pharLocation)) {
            // hack to prevent PHPCPD from starting in CLI mode and halting Phing
            eval("namespace SebastianBergmann\PHPCPD\CLI;
class Application
{
    public function run() {}
}");

            ob_start();
            include $this->pharLocation;
            ob_end_clean();

            if (class_exists('\\SebastianBergmann\\PHPCPD\\Detector\\Strategy\\DefaultStrategy')) {
                return;
            }
        }

        if (class_exists('Composer\\Autoload\\ClassLoader', false) && class_exists(
                '\\SebastianBergmann\\PHPCPD\\Detector\\Strategy\\DefaultStrategy'
            )
        ) {
            return;
        }

        if ($handler = @fopen('SebastianBergmann/PHPCPD/autoload.php', 'r', true)) {
            fclose($handler);
            @include_once 'SebastianBergmann/PHPCPD/autoload.php';

            if (version_compare(PHP_VERSION, '5.3.0') < 0) {
                throw new BuildException('The PHPCPD task now requires PHP 5.3+');
            }

            return;
        }

        if ($handler = @fopen('PHPCPD/Autoload.php', 'r', true)) {
            fclose($handler);
            @include_once 'PHPCPD/Autoload.php';

            $this->oldVersion = true;

            return;
        }

        throw new BuildException(
            'PHPCPDTask depends on PHPCPD being installed and on include_path.',
            $this->getLocation()
        );
    }

    /**
     * Executes PHPCPD against PhingFile or a FileSet
     *
     * @throws BuildException
     */
    public function main()
    {
        $this->loadDependencies();

        if (!isset($this->file) && count($this->filesets) == 0) {
            throw new BuildException('Missing either a nested fileset or attribute "file" set');
        }

        if (count($this->formatters) == 0) {
            // turn legacy format attribute into formatter
            $fmt = new PHPCPDFormatterElement($this);
            $fmt->setType($this->format);
            $fmt->setUseFile(false);

            $this->formatters[] = $fmt;
        }

        $this->validateFormatters();

        $filesToParse = array();

        if ($this->file instanceof PhingFile) {
            $filesToParse[] = $this->file->getPath();
        } else {
            // append any files in filesets
            foreach ($this->filesets as $fs) {
                $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles();

                foreach ($files as $filename) {
                    $f = new PhingFile($fs->getDir($this->project), $filename);
                    $filesToParse[] = $f->getAbsolutePath();
                }
            }
        }

        $this->log('Processing files...');

        if ($this->oldVersion) {
            $detectorClass = 'PHPCPD_Detector';
            $strategyClass = 'PHPCPD_Detector_Strategy_Default';
        } else {
            $detectorClass = '\\SebastianBergmann\\PHPCPD\\Detector\\Detector';
            $strategyClass = '\\SebastianBergmann\\PHPCPD\\Detector\\Strategy\\DefaultStrategy';
        }

        $detector = new $detectorClass(new $strategyClass());
        $clones = $detector->copyPasteDetection(
            $filesToParse,
            $this->minLines,
            $this->minTokens,
            $this->fuzzy
        );

        $this->log('Finished copy/paste detection');

        foreach ($this->formatters as $fe) {
            $formatter = $fe->getFormatter();
            $formatter->processClones(
                $clones,
                $this->project,
                $fe->getUseFile(),
                $fe->getOutfile()
            );
        }
    }

    /**
     * Validates the available formatters
     *
     * @throws BuildException
     */
    protected function validateFormatters()
    {
        foreach ($this->formatters as $fe) {
            if ($fe->getType() == '') {
                throw new BuildException('Formatter missing required "type" attribute.');
            }

            if ($fe->getUsefile() && $fe->getOutfile() === null) {
                throw new BuildException('Formatter requires "outfile" attribute when "useFile" is true.');
            }
        }
    }
}
<?php
/*
 *  $Id: 0165de67f7d92e363d668430c692ab7f3658dec6 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Wrapper to disable PHPCS's destructor
 *
 * @author  Michiel Rook <mrook@php.net>
 * @version $Id: 0165de67f7d92e363d668430c692ab7f3658dec6 $
 * @package phing.tasks.ext
 */
class PhpCodeSnifferTask_Wrapper extends PHP_CodeSniffer
{
    public function __destruct()
    {
        // override destructor
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Remove from cache files where contains errors
 *
 * @category  PHP
 * @package   PHP_CodeSniffer
 * @author    Rui Filipe Da Cunha Alves <ruifil@ruifil.com>
 */
class PHP_CodeSniffer_Reports_PhingRemoveFromCache implements PHP_CodeSniffer_Report
{
    /**
     * Cache data storage
     * @var DataStore
     */
    protected static $cache;

    /**
     * Set cache object
     *
     * @param DataStore $cache
     */
    public static function setCache($cache)
    {
        self::$cache = $cache;
    }

    /**
     * Remove file from cache if contains errors
     *
     * @param array                $report      Prepared report data.
     * @param PHP_CodeSniffer_File $phpcsFile   The file being reported on.
     * @param boolean              $showSources Show sources?
     * @param int                  $width       Maximum allowed line width.
     *
     * @return boolean
     */
    public function generateFileReport(
        $report,
        PHP_CodeSniffer_File $phpcsFile,
        $showSources = false,
        $width = 80
    ) {
        if (!self::$cache || ($report['errors'] === 0 && $report['warnings'] === 0)) {
            // Nothing to do
            return false;
        }

        self::$cache->remove($report['filename']);
        return false;
    }


    /**
     * Do nothing
     *
     * @param string  $cachedData    Any partial report data that was returned from
     *                               generateFileReport during the run.
     * @param int     $totalFiles    Total number of files processed during the run.
     * @param int     $totalErrors   Total number of errors found during the run.
     * @param int     $totalWarnings Total number of warnings found during the run.
     * @param int     $totalFixable  Total number of problems that can be fixed.
     * @param boolean $showSources   Show sources?
     * @param int     $width         Maximum allowed line width.
     * @param boolean $toScreen      Is the report being printed to screen?
     *
     * @return void
     */
    public function generate(
        $cachedData,
        $totalFiles,
        $totalErrors,
        $totalWarnings,
        $totalFixable,
        $showSources = false,
        $width = 80,
        $toScreen = true
    ) {
        // Do nothing
    }
}
<?php
/**
 * $Id: 33771f28a2886e13a8d3c38864d6758311c3e18d $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'PhpDocumentor/phpDocumentor/Errors.inc';

/**
 * Phing subclass of the ErrorTracker class provided with PhpDocumentor to work around limitations in PhpDocumentor API.
 *
 * This class is necessary because PhpDocumentor does directly output errors and
 * warnings occurred during testing for undocumented elements to stdout.
 * This class is injected globally to force PhpDocumentor to use phing's logging
 * mechanism.
 *
 * Obviously this is far from ideal, but there's also no solution given the inflexibility of the
 * PhpDocumentor design.
 *
 * @author Timo A. Hummel <privat@timohummel.com> @author felicitus
 * @version $Id: 33771f28a2886e13a8d3c38864d6758311c3e18d $
 * @package phing.tasks.ext.phpdoc
 */
class PhingPhpDocumentorErrorTracker extends ErrorTracker
{

    /**
     * @var object	Reference to the task we're called with
     */
    private $task;

    /**
     * Outputs a warning. This is an almost 1:1 copy from PhpDocumentor,
     * we're just processing the warning text and send it to phing's logger.
     *
     * @param $num integer    Number of parameters
     * @return nothing
     */
    public function addWarning($num)
    {
        $a = array('', '', '', '');
        if (func_num_args() > 1) {
            for ($i = 1; $i < func_num_args(); $i++) {
                $a[$i - 1] = func_get_arg($i);
            }
        }

        $message = sprintf($GLOBALS['phpDocumentor_warning_descrip'][$num], $a[0], $a[1], $a[2], $a[3]);
        $this->task->log($message, Project::MSG_WARN);

    }

    /**
     * Outputs an error. This is an almost 1:1 copy from PhpDocumentor,
     * we're just processing the error text and send it to phing's logger.
     *
     * @param $num integer    Number of parameters
     * @return nothing
     */

    public function addError($num)
    {
        $a = array('', '', '', '');
        if (func_num_args() > 1) {
            for ($i = 1; $i < func_num_args(); $i++) {
                $a[$i - 1] = func_get_arg($i);
            }
        }

        $message = sprintf($GLOBALS['phpDocumentor_error_descrip'][$num], $a[0], $a[1], $a[2], $a[3]);
        $this->task->log($message, Project::MSG_ERR);

    }

    /**
     * Sets the task we're working with. This is necessary since we need to be
     * able to call the method "log".
     *
     * @param  object  $task The task we're working with
     * @return nothing
     */
    public function setTask($task)
    {
        $this->task = $task;
    }

}
<?php
/**
 * $Id: 3c0527274eb0d0c2c6ed888ff23fe5b38367bb04 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'PhpDocumentor/phpDocumentor/Setup.inc.php';

/**
 * Phing subclass of the phpDocumentor_setup class provided with PhpDocumentor to work around limitations in PhpDocumentor API.
 *
 * This class is necessary because phpDocumentor_setup does not expose a complete API for setting configuration options.  Because
 * this class must directly modify some "private" GLOBAL(!) configuration variables, it is liable to break if the PhpDocumentor
 * internal implementation changes.  Obviously this is far from ideal, but there's also no solution given the inflexibility of the
 * PhpDocumentor design.
 *
 * @author Hans Lellelid <hans@xmpl.org>@author hans
 * @version $Id: 3c0527274eb0d0c2c6ed888ff23fe5b38367bb04 $
 * @package phing.tasks.ext.phpdoc
 */
class PhingPhpDocumentorSetup extends phpDocumentor_setup
{

    /**
     * Constructs a new PhingPhpDocumentorSetup.
     *
     * @param null $configdir
     * @param object $task The task we're working with, so we can pass it on to the ErrorTracker
     * @internal param string $configDir Directory in which to look for configuration files.
     */
    public function __construct($configdir = null, $task)
    {
        global $_phpDocumentor_cvsphpfile_exts, $_phpDocumentor_setting, $_phpDocumentor_phpfile_exts;

        $this->setup = new Io();
        $this->render = new phpDocumentor_IntermediateParser("Default Title");

        $GLOBALS['_phpDocumentor_install_dir'] = $configdir;
        $this->parseIni();

        // These redundant-looking lines seem to actually make a difference.
        // See: http://phing.info/trac/ticket/150
        $_phpDocumentor_phpfile_exts = $GLOBALS['_phpDocumentor_phpfile_exts'];
        $_phpDocumentor_cvsphpfile_exts = $GLOBALS['_phpDocumentor_cvsphpfile_exts'];

        if (tokenizer_ext) {
            $this->parse = new phpDocumentorTParser();
        } else {
            $this->parse = new Parser();
        }

        $this->setMemoryLimit();

        include_once 'phing/tasks/ext/phpdoc/PhingPhpDocumentorErrorTracker.php';

        // Inject our own error tracker to PhpDocumentor
        $GLOBALS['phpDocumentor_errors'] = new PhingPhpDocumentorErrorTracker();
        $GLOBALS['phpDocumentor_errors']->setTask($task);

    }

    /**
     * Set whether to generate sourcecode for each file parsed.
     *
     * This method exists as a hack because there is no API exposed for this in PhpDocumentor.
     * Note that because we are setting a "private" GLOBAL(!!) config var with this value, this
     * is subject to break if PhpDocumentor internals changes.
     *
     * @param bool $b
     */
    public function setGenerateSourcecode($b)
    {
        global $_phpDocumentor_setting;
        $_phpDocumentor_setting['sourcecode'] = (boolean) $b;
    }

    /**
     * Set an array of README/INSTALL/CHANGELOG file paths.
     *
     * This method exists as a hack because there is no API exposed for this in PhpDocumentor.
     * Note that because we are setting a "private" GLOBAL(!!) config var with this value, this
     * is subject to break if PhpDocumentor internals changes.
     *
     * @param array $files Absolute paths to files.
     */
    public function setRicFiles($files)
    {
        global $_phpDocumentor_RIC_files;
        $_phpDocumentor_RIC_files = $files;
    }

    /**
     * Set comma-separated list of tags to ignore.
     *
     * This method exists as a hack because there is no API exposed for this in PhpDocumentor.
     * Note that because we are setting a "private" GLOBAL(!!) config var with this value, this
     * is subject to break if PhpDocumentor internals changes.
     *
     * @param string $tags
     */
    public function setIgnoreTags($tags)
    {
        global $_phpDocumentor_setting;
        $ignoretags = explode(',', $tags);
        $ignoretags = array_map('trim', $ignoretags);
        $tags = array();
        foreach ($ignoretags as $tag) {
            if (!in_array(
                $tag,
                array('@global', '@access', '@package', '@ignore', '@name', '@param', '@return', '@staticvar', '@var')
            )
            ) {
                $tags[] = $tag;
            }
        }
        $_phpDocumentor_setting['ignoretags'] = $tags;
    }

    /**
     * Set whether to parse dirs as PEAR repos.
     *
     * This method exists as a hack because there is no API exposed for this in PhpDocumentor.
     * Note that because we are setting a "private" GLOBAL(!!) config var with this value, this
     * is subject to break if PhpDocumentor internals changes.
     *
     * @param bool $b
     */
    public function setPear($b)
    {
        global $_phpDocumentor_setting;
        $_phpDocumentor_setting['pear'] = (boolean) $b;
    }

    /**
     * Set fullpath to directory to look in for examples.
     *
     * This method exists as a hack because there is no API exposed for this in PhpDocumentor.
     * Note that because we are setting a "private" GLOBAL(!!) config var with this value, this
     * is subject to break if PhpDocumentor internals changes.
     *
     * @param string $dir
     */
    public function setExamplesDir($dir)
    {
        global $_phpDocumentor_setting;
        $_phpDocumentor_setting['examplesdir'] = $dir;
    }

    /**
     * Sets the default package name.
     *
     * This method exists as a hack because there is no API exposed for this in PhpDocumentor.
     * Note that because we are setting a "private" GLOBAL(!!) config var with this value, this
     * is subject to break if PhpDocumentor internals changes.
     *
     * @param string $name
     */
    public function setDefaultPackageName($name)
    {
        $GLOBALS['phpDocumentor_DefaultPackageName'] = trim($name);
    }

    /**
     * Sets the default category name.
     *
     * This method exists as a hack because there is no API exposed for this in PhpDocumentor.
     * Note that because we are setting a "private" GLOBAL(!!) config var with this value, this
     * is subject to break if PhpDocumentor internals changes.
     *
     * @param string $name
     */
    public function setDefaultCategoryName($name)
    {
        $GLOBALS['phpDocumentor_DefaultCategoryName'] = trim($name);
    }

    /**
     * Enables quiet mode.
     *
     * This method exists as a hack because the API exposed for this method in PhpDocumentor
     * doesn't work correctly.
     *
     * Note that because we are setting a "private" GLOBAL(!!) config var with this value, this
     * is subject to break if PhpDocumentor internals changes.
     *
     */
    public function setQuietMode()
    {
        global $_phpDocumentor_setting;
        $_phpDocumentor_setting['quiet'] = true;
        parent::setQuietMode();
    }

    /**
     * Control whether or not warnings will be shown for undocumented elements.
     * Useful for identifying classes and methods that haven't yet been
     * documented.
     *
     * @param bool $bEnable
     */
    public function setUndocumentedelements($bEnable)
    {
        $this->render->setUndocumentedElementWarningsMode($bEnable);
    }

    /**
     * custom tags, will be recognized and put in tags[] instead of
     * unknowntags[]
     *
     * This method exists as a hack because the API exposed for this method in
     * PhpDocumentor doesn't work correctly.
     *
     * Note that because we are setting a "private" GLOBAL(!!) config var with
     * this value, this is subject to break if PhpDocumentor internals changes.
     *
     * @param string $sCustomtags
     */
    public function setCustomtags($sCustomtags)
    {
        global $_phpDocumentor_setting;
        $_phpDocumentor_setting['customtags'] = $sCustomtags;
    }

    /**
     * Files to ignore
     *
     * @param string $sIgnore
     */
    public function setIgnore($sIgnore)
    {
        global $_phpDocumentor_setting;
        $_phpDocumentor_setting['ignore'] = $sIgnore;
    }
}
<?php
/*
 *  $Id: fff99666d63c88940f9b305c2822856fa00ab5ce $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * PhpDocumentor2 Task (http://www.phpdoc.org)
 * Based on the DocBlox Task
 *
 * @author    Michiel Rook <mrook@php.net>
 * @version   $Id: fff99666d63c88940f9b305c2822856fa00ab5ce $
 * @since     2.4.10
 * @package   phing.tasks.ext.phpdoc
 */
class PhpDocumentor2Task extends Task
{
    /**
     * List of filesets
     * @var FileSet[]
     */
    private $filesets = array();

    /**
     * Destination/target directory
     * @var PhingFile
     */
    private $destDir = null;

    /**
     * name of the template to use
     * @var string
     */
    private $template = "responsive-twig";

    /**
     * Title of the project
     * @var string
     */
    private $title = "API Documentation";

    /**
     * Name of default package
     * @var string
     */
    private $defaultPackageName = "Default";

    /**
     * Path to the phpDocumentor .phar
     * @var string
     */
    private $pharLocation = '';

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Sets destination/target directory
     * @param PhingFile $destDir
     */
    public function setDestDir(PhingFile $destDir)
    {
        $this->destDir = $destDir;
    }

    /**
     * Convenience setter (@see setDestDir)
     * @param PhingFile $output
     */
    public function setOutput(PhingFile $output)
    {
        $this->destDir = $output;
    }

    /**
     * Sets the template to use
     * @param strings $template
     */
    public function setTemplate($template)
    {
        $this->template = (string) $template;
    }

    /**
     * Sets the title of the project
     * @param strings $title
     */
    public function setTitle($title)
    {
        $this->title = (string) $title;
    }

    /**
     * Sets the default package name
     * @param string $defaultPackageName
     */
    public function setDefaultPackageName($defaultPackageName)
    {
        $this->defaultPackageName = (string) $defaultPackageName;
    }

    /**
     * @param string $pharLocation
     */
    public function setPharLocation($pharLocation)
    {
        $this->pharLocation = $pharLocation;
    }

    /**
     * Forces phpDocumentor to be quiet
     * @deprecated
     * @param boolean $quiet
     */
    public function setQuiet($quiet)
    {
        $this->project->log(__CLASS__ . ": the 'quiet' option has been deprecated", Project::MSG_WARN);
    }

    /**
     * Task entry point
     * @see Task::main()
     */
    public function main()
    {
        if (empty($this->destDir)) {
            throw new BuildException("You must supply the 'destdir' attribute", $this->getLocation());
        }

        if (empty($this->filesets)) {
            throw new BuildException("You have not specified any files to include (<fileset>)", $this->getLocation());
        }

        if (version_compare(PHP_VERSION, '5.3.0') < 0) {
            throw new BuildException("The phpdocumentor2 task requires PHP 5.3+");
        }

        require_once 'phing/tasks/ext/phpdoc/PhpDocumentor2Wrapper.php';

        $wrapper = new PhpDocumentor2Wrapper();
        $wrapper->setProject($this->project);
        $wrapper->setFilesets($this->filesets);
        $wrapper->setDestDir($this->destDir);
        $wrapper->setTemplate($this->template);
        $wrapper->setTitle($this->title);
        $wrapper->setDefaultPackageName($this->defaultPackageName);
        $wrapper->setPharLocation($this->pharLocation);
        $wrapper->run();
    }
}
<?php
/*
 *  $Id: 9f22f2863b649ddf3820399fd5738c58900b3a24 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Wrapper around PhpDocumentor2 (so we retain
 * PHP 5.2 compatibility in the main task)
 *
 * @author    Michiel Rook <mrook@php.net>
 * @version   $Id: 9f22f2863b649ddf3820399fd5738c58900b3a24 $
 * @since     2.4.10
 * @package   phing.tasks.ext.phpdoc
 */
class PhpDocumentor2Wrapper
{
    /**
     * Phing project instance
     * @var Project
     */
    private $project = null;

    /**
     * List of filesets
     * @var FileSet[]
     */
    private $filesets = array();

    /**
     * Destination/target directory
     * @var PhingFile
     */
    private $destDir = null;

    /**
     * name of the template to use
     * @var string
     */
    private $template = "responsive-twig";

    /**
     * Title of the project
     * @var string
     */
    private $title = "API Documentation";

    /**
     * Name of the default package
     * @var string
     */
    private $defaultPackageName = "Default";

    /**
     * Path to the phpDocumentor 2 source
     * @var string
     */
    private $phpDocumentorPath = "";

    /**
     * Path to the phpDocumentor .phar
     * @var string
     */
    private $pharLocation = '';

    /**
     * @var \phpDocumentor\Application
     */
    private $app = null;

    /**
     * Sets project instance
     *
     * @param Project $project
     */
    public function setProject($project)
    {
        $this->project = $project;
    }

    /**
     * Sets filesets array
     *
     * @param FileSet[] $filesets
     */
    public function setFilesets($filesets)
    {
        $this->filesets = $filesets;
    }

    /**
     * Sets destination/target directory
     * @param PhingFile $destDir
     */
    public function setDestDir(PhingFile $destDir)
    {
        $this->destDir = $destDir;
    }

    /**
     * Sets the template to use
     * @param string $template
     */
    public function setTemplate($template)
    {
        $this->template = (string) $template;
    }

    /**
     * Sets the title of the project
     * @param string $title
     */
    public function setTitle($title)
    {
        $this->title = (string) $title;
    }

    /**
     * Sets the default package name
     * @param string $defaultPackageName
     */
    public function setDefaultPackageName($defaultPackageName)
    {
        $this->defaultPackageName = (string) $defaultPackageName;
    }

    /**
     * @param string $pharLocation
     */
    public function setPharLocation($pharLocation)
    {
        $this->pharLocation = $pharLocation;
    }

    /**
     * Finds and initializes the phpDocumentor installation
     */
    private function initializePhpDocumentor()
    {
        $phpDocumentorPath = '';

        if (!empty($this->pharLocation)) {
            include_once 'phar://' . $this->pharLocation . '/vendor/autoload.php';

            if (!class_exists('phpDocumentor\\Bootstrap')) {
                throw new BuildException(
                    $this->pharLocation . ' does not look like a phpDocumentor 2 .phar'
                );
            }
        } elseif (class_exists('Composer\\Autoload\\ClassLoader', false)) {
            if (!class_exists('phpDocumentor\\Bootstrap')) {
                throw new BuildException('You need to install phpDocumentor 2 or add your include path to your composer installation.');
            }
        } else {
            $phpDocumentorPath = $this->findPhpDocumentorPath();

            if (empty($phpDocumentorPath)) {
                throw new BuildException("Please make sure phpDocumentor 2 is installed and on the include_path.");
            }

            set_include_path($phpDocumentorPath . PATH_SEPARATOR . get_include_path());

            require_once $phpDocumentorPath . '/phpDocumentor/Bootstrap.php';
        }

        $this->app = \phpDocumentor\Bootstrap::createInstance()->initialize();

        $this->phpDocumentorPath = $phpDocumentorPath;
    }

    /**
     * Build a list of files (from the fileset elements)
     * and call the phpDocumentor parser
     *
     * @return string
     */
    private function parseFiles()
    {
        $parser = $this->app['parser'];
        $builder = $this->app['descriptor.builder'];

        $builder->createProjectDescriptor();
        $projectDescriptor = $builder->getProjectDescriptor();
        $projectDescriptor->setName($this->title);

        $paths = array();

        // filesets
        foreach ($this->filesets as $fs) {
            $ds = $fs->getDirectoryScanner($this->project);
            $dir = $fs->getDir($this->project);
            $srcFiles = $ds->getIncludedFiles();

            foreach ($srcFiles as $file) {
                $paths[] = $dir . FileSystem::getFileSystem()->getSeparator() . $file;
            }
        }

        $this->project->log("Will parse " . count($paths) . " file(s)", Project::MSG_VERBOSE);

        $files = new \phpDocumentor\Fileset\Collection();
        $files->addFiles($paths);

        $mapper = new \phpDocumentor\Descriptor\Cache\ProjectDescriptorMapper($this->app['descriptor.cache']);
        $mapper->garbageCollect($files);
        $mapper->populate($projectDescriptor);

        $parser->setPath($files->getProjectRoot());
        $parser->setDefaultPackageName($this->defaultPackageName);

        $parser->parse($builder, $files);

        $mapper->save($projectDescriptor);

        return $mapper;
    }

    /**
     * Transforms the parsed files
     */
    private function transformFiles()
    {
        $transformer = $this->app['transformer'];
        $compiler = $this->app['compiler'];
        $builder = $this->app['descriptor.builder'];
        $projectDescriptor = $builder->getProjectDescriptor();

        $transformer->getTemplates()->load($this->template, $transformer);
        $transformer->setTarget($this->destDir->getAbsolutePath());

        foreach ($compiler as $pass) {
            $pass->execute($projectDescriptor);
        }
    }

    /**
     * Runs phpDocumentor 2
     */
    public function run()
    {
        $this->initializePhpDocumentor();

        $cache = $this->app['descriptor.cache'];
        $cache->getOptions()->setCacheDir($this->destDir->getAbsolutePath());

        $this->parseFiles();

        $this->project->log("Transforming...", Project::MSG_VERBOSE);

        $this->transformFiles();
    }

    /**
     * Find the correct php documentor path
     *
     * @return null|string
     */
    private function findPhpDocumentorPath()
    {
        $phpDocumentorPath = null;
        $directories = array('phpDocumentor', 'phpdocumentor');
        foreach ($directories as $directory) {
            foreach (Phing::explodeIncludePath() as $path) {
                $testPhpDocumentorPath = $path . DIRECTORY_SEPARATOR . $directory . DIRECTORY_SEPARATOR . 'src';
                if (file_exists($testPhpDocumentorPath)) {
                    $phpDocumentorPath = $testPhpDocumentorPath;
                }
            }
        }

        return $phpDocumentorPath;
    }
}
<?php

/**
 * $Id: b72762694ae5d5619ecebf9b66c08bd6ff868821 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/phpdoc/PhpDocumentorTask.php';

/**
 * Task to run phpDocumentor with an external process
 *
 * This classes uses the commandline phpdoc script to build documentation.
 * Use this task instead of the PhpDocumentorTask when you've a clash with the
 * Smarty libraries.
 *
 * @author Michiel Rook <mrook@php.net>
 * @author Markus Fischer <markus@fischer.name>
 * @version $Id: b72762694ae5d5619ecebf9b66c08bd6ff868821 $
 * @package phing.tasks.ext.phpdoc
 */
class PhpDocumentorExternalTask extends PhpDocumentorTask
{
    /**
     * The path to the executable for phpDocumentor
     */
    protected $programPath = 'phpdoc';

    protected $sourcepath = null;

    /**
     * @var bool  ignore symlinks to other files or directories
     */
    protected $ignoresymlinks = false;

    /**
     * Sets the path to the phpDocumentor executable
     * @param $programPath
     */
    public function setProgramPath($programPath)
    {
        $this->programPath = $programPath;
    }

    /**
     * Returns the path to the phpDocumentor executable
     */
    public function getProgramPath()
    {
        return $this->programPath;
    }

    /**
     * Set the source path. A directory or a comma separate list of directories.
     * @param $sourcepath
     */
    public function setSourcepath($sourcepath)
    {
        $this->sourcepath = $sourcepath;
    }

    /**
     * Ignore symlinks to other files or directories.
     *
     * @param bool $bSet
     */
    public function setIgnoresymlinks($bSet)
    {
        $this->ignoresymlinks = $bSet;
    }

    /**
     * Main entrypoint of the task
     */
    public function main()
    {
        $this->validate();
        $arguments = join(' ', $this->constructArguments());

        $this->log("Running phpDocumentor...");

        exec($this->programPath . " " . $arguments, $output, $return);

        if ($return != 0) {
            throw new BuildException("Could not execute phpDocumentor: " . implode(' ', $output));
        }

        foreach ($output as $line) {
            if (strpos($line, 'ERROR') !== false) {
                $this->log($line, Project::MSG_ERR);
                continue;
            }

            $this->log($line, Project::MSG_VERBOSE);
        }
    }

    /**
     * Constructs an argument string for phpDocumentor
     * @return array
     */
    protected function constructArguments()
    {
        $aArgs = array();
        if ($this->title) {
            $aArgs[] = '--title "' . $this->title . '"';
        }

        if ($this->destdir) {
            $aArgs[] = '--target "' . $this->destdir->getAbsolutePath() . '"';
        }

        if ($this->sourcepath) {
            $aArgs[] = '--directory "' . $this->sourcepath . '"';
        }

        if ($this->output) {
            $aArgs[] = '--output ' . $this->output;
        }

        if ($this->linksource) {
            $aArgs[] = '--sourcecode on';
        }

        if ($this->parseprivate) {
            $aArgs[] = '--parseprivate on';
        }

        if ($this->ignore) {
            $aArgs[] = '--ignore ' . $this->ignore;
        }

        // append any files in filesets
        $filesToParse = array();
        foreach ($this->filesets as $fs) {
            $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles();
            foreach ($files as $filename) {
                $f = new PhingFile($fs->getDir($this->project), $filename);
                $filesToParse[] = $f->getAbsolutePath();
            }
        }
        if (count($filesToParse) > 0) {
            $aArgs[] = '--filename "' . join(',', $filesToParse) . '"';
        }

        // append any files in filesets
        $ricFiles = array();
        foreach ($this->projDocFilesets as $fs) {
            $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles();
            foreach ($files as $filename) {
                $f = new PhingFile($fs->getDir($this->project), $filename);
                $ricFiles[] = $f->getAbsolutePath();
            }
        }
        if (count($ricFiles) > 0) {
            $aArgs[] = '--readmeinstallchangelog "' .
                join(',', $ricFiles) . '"';
        }

        if ($this->javadocDesc) {
            $aArgs[] = '--javadocdesc on';
        }

        if ($this->quiet) {
            $aArgs[] = '--quiet on';
        }

        if ($this->packages) {
            $aArgs[] = '--packageoutput "' . $this->packages . '"';
        }

        if ($this->ignoreTags) {
            $aArgs[] = '--ignore-tags "' . $this->ignoreTags . '"';
        }

        if ($this->defaultCategoryName) {
            $aArgs[] = '--defaultcategoryname "' . $this->defaultCategoryName .
                '"';
        }

        if ($this->examplesDir) {
            $aArgs[] = '--examplesdir "' . $this->examplesDir->getAbsolutePath()
                . '"';
        }

        if ($this->templateBase) {
            $aArgs[] = '--templatebase "' . $this->templateBase->getAbsolutePath()
                . '"';
        }

        if ($this->pear) {
            $aArgs[] = '--pear on';
        }

        if ($this->undocumentedelements) {
            $aArgs[] = '--undocumentedelements on';
        }

        if ($this->customtags) {
            $aArgs[] = '--customtags "' . $this->customtags . '"';
        }

        if ($this->ignoresymlinks) {
            $aArgs[] = '--ignoresymlinks on';
        }

        return $aArgs;
    }

    /**
     * Override PhpDocumentorTask::init() because they're specific to the phpdoc
     * API which we don't use.
     */
    public function init()
    {
    }

    /**
     * Validates that necessary minimum options have been set. Based on
     * PhpDocumentorTask::validate().
     */
    protected function validate()
    {
        if (!$this->destdir) {
            throw new BuildException("You must specify a destdir for phpdoc.",
                $this->getLocation());
        }
        if (!$this->output) {
            throw new BuildException("You must specify an output format for " .
                "phpdoc (e.g. HTML:frames:default).", $this->getLocation());
        }
        if (empty($this->filesets) && !$this->sourcepath) {
            throw new BuildException("You have not specified any files to " .
                "include (<fileset> or sourcepath attribute) for phpdoc.",
                $this->getLocation());
        }
        if ($this->configdir) {
            $this->log(
                'Ignoring unsupported configdir-Attribute',
                Project::MSG_VERBOSE
            );
        }
    }
}

;
<?php

/**
 * $Id: 11e97cff8a9f2331750049c1c8fe4fb57f3cc962 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Task to run PhpDocumentor.
 *
 * @author Hans Lellelid <hans@xmpl.org>
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 11e97cff8a9f2331750049c1c8fe4fb57f3cc962 $
 * @package phing.tasks.ext.phpdoc
 */
class PhpDocumentorTask extends Task
{

    /**
     * @var string Title for browser window / package index.
     */
    protected $title;

    /**
     * @var PhingFile The target directory for output files.
     */
    protected $destdir;

    /**
     * @var array FileSet[] Filesets for files to parse.
     */
    protected $filesets = array();

    /**
     * @var array FileSet[] Project documentation (README/INSTALL/CHANGELOG) files.
     */
    protected $projDocFilesets = array();

    /**
     * @var string Package output format.
     */
    protected $output;

    /**
     * @var boolean Whether to generate sourcecode for each file parsed.
     */
    protected $linksource = false;

    /**
     * @var boolean Whether to parse private members.
     */
    protected $parsePrivate = false;

    /**
     * @var boolean Whether to use javadoc descriptions (more primitive).
     */
    protected $javadocDesc = false;

    /**
     * @var PhingFile Base directory for locating template files.
     */
    protected $templateBase;

    /**
     * @var boolean Wheter to suppress output.
     */
    protected $quiet = false;

    /**
     * @var string Comma-separated list of packages to output.
     */
    protected $packages;

    /**
     * @var string Comma-separated list of tags to ignore.
     */
    protected $ignoreTags;

    /**
     * @var string Default package name.
     */
    protected $defaultPackageName;

    /**
     * @var string Default category name.
     */
    protected $defaultCategoryName;

    /**
     * @var PhingFile Directory in which to look for examples.
     */
    protected $examplesDir;

    /**
     * @var PhingFile Directory in which to look for configuration files.
     */
    protected $configDir;

    /**
     * @var boolean Whether to parse as a PEAR repository.
     */
    protected $pear = false;

    /**
     * @var boolean Control whether or not warnings will be shown for
     *              undocumented elements. Useful for identifying classes and
     *              methods that haven't yet been documented.
     */
    protected $undocumentedelements = false;

    /**
     * @var string  custom tags, will be recognized and put in tags[] instead of
     *              unknowntags[].
     */
    protected $customtags = '';

    /**
     * @var string  files to ignore
     */
    protected $ignore = '';

    /**
     * Set the title for the generated documentation
     * @param $title
     */
    public function setTitle($title)
    {
        $this->title = $title;
    }

    /**
     * Set the destination directory for the generated documentation
     * @param PhingFile $destdir
     */
    public function setDestdir(PhingFile $destdir)
    {
        $this->destdir = $destdir;
    }

    /**
     * Alias for {@link setDestdir()}.
     * @see setDestdir()
     * @param PhingFile $destdir
     */
    public function setTarget(PhingFile $destdir)
    {
        $this->setDestdir($destdir);
    }

    /**
     * Set the output format (e.g. HTML:Smarty:PHP).
     * @param string $output
     */
    public function setOutput($output)
    {
        $this->output = $output;
    }

    /**
     * Set whether to generate sourcecode for each file parsed
     * @param boolean
     */
    public function setSourcecode($b)
    {
        $this->setLinksource($b);
    }

    /**
     * Set whether to generate sourcecode for each file parsed
     * @param boolean
     */
    public function setLinksource($b)
    {
        $this->linksource = $b;
    }

    /**
     * Set whether to suppress output.
     * @param boolean $b
     */
    public function setQuiet($b)
    {
        $this->quiet = $b;
    }

    /**
     * Should private members/classes be documented
     * @param boolean
     */
    public function setParseprivate($parseprivate)
    {
        $this->parsePrivate = $parseprivate;
    }

    /**
     * Whether to use javadoc descriptions (more primitive).
     * @param boolean
     */
    public function setJavadocdesc($javadoc)
    {
        $this->javadocDesc = $javadoc;
    }

    /**
     * Set (comma-separated) list of packages to output.
     *
     * @param string $packages
     */
    public function setPackageoutput($packages)
    {
        $this->packages = $packages;
    }

    /**
     * Set (comma-separated) list of tags to ignore.
     *
     * @param string $tags
     */
    public function setIgnoretags($tags)
    {
        $this->ignoreTags = $tags;
    }

    /**
     * Set a directory to search for examples in.
     * @param PhingFile $d
     */
    public function setExamplesdir(PhingFile $d)
    {
        $this->examplesDir = $d;
    }

    /**
     * Set a directory to search for configuration files in.
     * @param PhingFile $d
     */
    public function setConfigdir(PhingFile $d)
    {
        $this->configDir = $d;
    }

    /**
     * Sets the default package name.
     * @param string $name
     */
    public function setDefaultpackagename($name)
    {
        $this->defaultPackageName = $name;
    }

    /**
     * Sets the default category name.
     * @param string $name
     */
    public function setDefaultcategoryname($name)
    {
        $this->defaultCategoryName = $name;
    }

    /**
     * Set whether to parse as PEAR repository.
     * @param boolean $b
     */
    public function setPear($b)
    {
        $this->pear = $b;
    }

    /**
     * Creates a FileSet.
     * @return FileSet
     */
    public function createFileset()
    {
        $num = array_push($this->filesets, new FileSet());

        return $this->filesets[$num - 1];
    }

    /**
     * Creates a readme/install/changelog fileset.
     * @return FileSet
     */
    public function createProjdocfileset()
    {
        $num = array_push($this->projDocFilesets, new FileSet());

        return $this->projDocFilesets[$num - 1];
    }

    /**
     * Control whether or not warnings will be shown for undocumented elements.
     * Useful for identifying classes and methods that haven't yet been
     * documented.
     * @param boolean $b
     */
    public function setUndocumentedelements($b)
    {
        $this->undocumentedelements = $b;
    }

    /**
     * custom tags, will be recognized and put in tags[] instead of
     * unknowntags[].
     *
     * @param string $sCustomtags
     */
    public function setCustomtags($sCustomtags)
    {
        $this->customtags = $sCustomtags;
    }

    /**
     * Set base location of all templates for this parse.
     *
     * @param PhingFile $oTemplateBase
     * @internal param PhingFile $destdir
     */
    public function setTemplateBase(PhingFile $oTemplateBase)
    {
        $this->templateBase = $oTemplateBase;
    }

    /**
     * Set files to ignore
     * @param string $sIgnore
     */
    public function setIgnore($sIgnore)
    {
        $this->ignore = $sIgnore;
    }

    /**
     * Searches include_path for PhpDocumentor install and adjusts include_path appropriately.
     * @throws BuildException - if unable to find PhpDocumentor on include_path
     */
    protected function findPhpDocumentorInstall()
    {
        $found = null;
        foreach (Phing::explodeIncludePath() as $path) {
            $testpath = $path . DIRECTORY_SEPARATOR . 'PhpDocumentor';
            if (file_exists($testpath)) {
                $found = $testpath;
                break;
            }
        }
        if (!$found) {
            throw new BuildException("PhpDocumentor task depends on PhpDocumentor being installed and on include_path.", $this->getLocation(
            ));
        }
        // otherwise, adjust the include_path to path to include the PhpDocumentor directory ...
        set_include_path(get_include_path() . PATH_SEPARATOR . $found);
        include_once "phpDocumentor/Setup.inc.php";
        if (!class_exists('phpDocumentor_setup')) {
            throw new BuildException("Error including PhpDocumentor setup class file.");
        }
    }

    /**
     * Main entrypoint of the task
     * Loads the necessary environment for running PhpDoc, then runs PhpDoc
     *
     * @throws BuildException - if the phpdoc classes can't be loaded.
     */
    public function main()
    {
        $this->findPhpDocumentorInstall();
        include_once 'phing/tasks/ext/phpdoc/PhingPhpDocumentorSetup.php';

        $this->validate();
        $configdir = $this->configDir ? $this->configDir->getAbsolutePath() : null;
        $phpdoc = new PhingPhpDocumentorSetup($configdir, $this);
        $this->setPhpDocumentorOptions($phpdoc);
        //$phpdoc->readCommandLineSettings();
        $phpdoc->setupConverters($this->output);
        $phpdoc->createDocs();
    }

    /**
     * Validates that necessary minimum options have been set.
     * @throws BuildException if validation doesn't pass
     */
    protected function validate()
    {
        if (!$this->destdir) {
            throw new BuildException("You must specify a destdir for phpdoc.", $this->getLocation());
        }
        if (!$this->output) {
            throw new BuildException("You must specify an output format for phpdoc (e.g. HTML:frames:default).", $this->getLocation(
            ));
        }
        if (empty($this->filesets)) {
            throw new BuildException("You have not specified any files to include (<fileset>) for phpdoc.", $this->getLocation(
            ));
        }
    }

    /**
     * Sets the options on the passed-in phpdoc setup object.
     * @param PhingPhpDocumentorSetup $phpdoc
     */
    protected function setPhpDocumentorOptions(PhingPhpDocumentorSetup $phpdoc)
    {

        // Title MUST be set first ... (because it re-initializes the internal state of the PhpDocu renderer)
        if ($this->title) {
            $phpdoc->setTitle($this->title);
        }

        if ($this->parsePrivate) {
            $phpdoc->setParsePrivate();
        }

        if ($this->javadocDesc) {
            $phpdoc->setJavadocDesc();
        }

        if ($this->quiet) {
            $phpdoc->setQuietMode();
        }

        if ($this->destdir) {
            $phpdoc->setTargetDir($this->destdir->getAbsolutePath());
        }

        if ($this->packages) {
            $phpdoc->setPackageOutput($this->packages);
        }

        if ($this->templateBase) {
            $phpdoc->setTemplateBase($this->templateBase->getAbsolutePath());
        }

        if ($this->linksource) {
            $phpdoc->setGenerateSourcecode($this->linksource);
        }

        if ($this->examplesDir) {
            $phpdoc->setExamplesDir($this->examplesDir->getAbsolutePath());
        }

        if ($this->ignoreTags) {
            $phpdoc->setIgnoreTags($this->ignoreTags);
        }

        if ($this->defaultPackageName) {
            $phpdoc->setDefaultPackageName($this->defaultPackageName);
        }

        if ($this->defaultCategoryName) {
            $phpdoc->setDefaultCategoryName($this->defaultCategoryName);
        }

        if ($this->pear) {
            $phpdoc->setPear($this->pear);
        }

        if ($this->ignore) {
            $phpdoc->setIgnore($this->ignore);
        }

        // append any files in filesets
        $filesToParse = array();
        foreach ($this->filesets as $fs) {
            $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles();
            foreach ($files as $filename) {
                $f = new PhingFile($fs->getDir($this->project), $filename);
                $filesToParse[] = $f->getAbsolutePath();
            }
        }
        //print_r(implode(",", $filesToParse));
        $phpdoc->setFilesToParse(implode(",", $filesToParse));

        // append any files in filesets
        $ricFiles = array();
        foreach ($this->projDocFilesets as $fs) {
            $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles();
            foreach ($files as $filename) {
                $f = new PhingFile($fs->getDir($this->project), $filename);
                $ricFiles[] = $f->getName();
            }
        }
        $phpdoc->setRicFiles($ricFiles);

        if ($this->undocumentedelements) {
            $phpdoc->setUndocumentedelements($this->undocumentedelements);
        }

        if ($this->customtags) {
            $phpdoc->setCustomtags($this->customtags);
        }
    }
}
<?php
/*
 *  $Id: 6352a87c4c3b940a0afd55b21645206a23960e0b $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/util/DataStore.php';
require_once 'phing/system/io/FileWriter.php';

/**
 * A PHP lint task. Checking syntax of one or more PHP source file.
 *
 * @author   Knut Urdalen <knut.urdalen@telio.no>
 * @author   Stefan Priebsch <stefan.priebsch@e-novative.de>
 * @version  $Id: 6352a87c4c3b940a0afd55b21645206a23960e0b $
 * @package  phing.tasks.ext
 */
class PhpLintTask extends Task
{

    protected $file; // the source file (from xml attribute)
    protected $filesets = array(); // all fileset objects assigned to this task

    protected $errorProperty;
    protected $haltOnFailure = false;
    protected $hasErrors = false;
    protected $badFiles = array();
    protected $interpreter = ''; // php interpreter to use for linting

    protected $logLevel = Project::MSG_VERBOSE;

    protected $cache = null;

    protected $tofile = null;

    protected $deprecatedAsError = false;

    /**
     * Initialize the interpreter with the Phing property php.interpreter
     */
    public function init()
    {
        $this->setInterpreter($this->project->getProperty('php.interpreter'));
    }

    /**
     * Override default php interpreter
     * @todo    Do some sort of checking if the path is correct but would
     *          require traversing the systems executeable path too
     * @param string $sPhp
     */
    public function setInterpreter($sPhp)
    {
        if (strpos($sPhp, ' ') !== false) {
            $sPhp = escapeshellarg($sPhp);
        }
        $this->interpreter = $sPhp;
    }

    /**
     * The haltonfailure property
     * @param boolean $aValue
     */
    public function setHaltOnFailure($aValue)
    {
        $this->haltOnFailure = $aValue;
    }

    /**
     * File to be performed syntax check on
     * @param PhingFile $file
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * Set an property name in which to put any errors.
     * @param string $propname
     */
    public function setErrorproperty($propname)
    {
        $this->errorProperty = $propname;
    }

    /**
     * Whether to store last-modified times in cache
     *
     * @param PhingFile $file
     */
    public function setCacheFile(PhingFile $file)
    {
        $this->cache = new DataStore($file);
    }

    /**
     * File to save error messages to
     *
     * @param PhingFile $tofile
     * @internal param PhingFile $file
     */
    public function setToFile(PhingFile $tofile)
    {
        $this->tofile = $tofile;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Set level of log messages generated (default = info)
     * @param string $level
     */
    public function setLevel($level)
    {
        switch ($level) {
            case "error":
                $this->logLevel = Project::MSG_ERR;
                break;
            case "warning":
                $this->logLevel = Project::MSG_WARN;
                break;
            case "info":
                $this->logLevel = Project::MSG_INFO;
                break;
            case "verbose":
                $this->logLevel = Project::MSG_VERBOSE;
                break;
            case "debug":
                $this->logLevel = Project::MSG_DEBUG;
                break;
        }
    }

    /**
     * Sets whether to treat deprecated warnings (introduced in PHP 5.3) as errors
     * @param boolean $deprecatedAsError
     */
    public function setDeprecatedAsError($deprecatedAsError)
    {
        $this->deprecatedAsError = $deprecatedAsError;
    }

    /**
     * Execute lint check against PhingFile or a FileSet
     */
    public function main()
    {
        if (!isset($this->file) and count($this->filesets) == 0) {
            throw new BuildException("Missing either a nested fileset or attribute 'file' set");
        }

        if ($this->file instanceof PhingFile) {
            $this->lint($this->file->getPath());
        } else { // process filesets
            $project = $this->getProject();
            foreach ($this->filesets as $fs) {
                $ds = $fs->getDirectoryScanner($project);
                $files = $ds->getIncludedFiles();
                $dir = $fs->getDir($this->project)->getPath();
                foreach ($files as $file) {
                    $this->lint($dir . DIRECTORY_SEPARATOR . $file);
                }
            }
        }

        // write list of 'bad files' to file (if specified)
        if ($this->tofile) {
            $writer = new FileWriter($this->tofile);

            foreach ($this->badFiles as $file => $messages) {
                foreach ($messages as $msg) {
                    $writer->write($file . "=" . $msg . PHP_EOL);
                }
            }

            $writer->close();
        }

        $message = '';
        foreach ($this->badFiles as $file => $messages) {
            foreach ($messages as $msg) {
                $message .= $file . "=" . $msg . PHP_EOL;
            }
        }

        // save list of 'bad files' with errors to property errorproperty (if specified)
        if ($this->errorProperty) {
            $this->project->setProperty($this->errorProperty, $message);
        }

        if (!empty($this->cache)) {
            $this->cache->commit();
        }

        if ($this->haltOnFailure && $this->hasErrors) {
            throw new BuildException('Syntax error(s) in PHP files: ' . $message);
        }
    }

    /**
     * Performs the actual syntax check
     *
     * @param  string $file
     * @throws BuildException
     * @return void
     */
    protected function lint($file)
    {
        $command = $this->interpreter == ''
            ? 'php'
            : $this->interpreter;

        if (strpos($command, 'hhvm') !== false) {
            $command .= ' --no-config -l';
        } else {
            if ($this->deprecatedAsError) {
                $command .= ' -d error_reporting=32767 ';
            }

            $command .= ' -n -l ';
        }

        if (! file_exists($file)) {
            throw new BuildException('File not found: ' . $file);
        }

        if (! is_readable($file)) {
            throw new BuildException('Permission denied: ' . $file);
        }

        if ($this->cache) {
            $lastmtime = $this->cache->get($file);

            if ($lastmtime >= filemtime($file)) {
                $this->log("Not linting '" . $file . "' due to cache", Project::MSG_DEBUG);

                return false;
            }
        }

        $messages = array();
        $errorCount = 0;

        exec($command . '"' . $file . '" 2>&1', $messages);

        for ($i = 0; $i < count($messages); $i++) {
            $message = $messages[$i];
            if (trim($message) == '') {
                continue;
            }

            if ((!preg_match('/^(.*)Deprecated:/', $message) || $this->deprecatedAsError) && !preg_match(
                    '/^No syntax errors detected/',
                    $message
                )
            ) {
                $this->log($message, Project::MSG_ERR);

                if (!isset($this->badFiles[$file])) {
                    $this->badFiles[$file] = array();
                }

                array_push($this->badFiles[$file], $message);

                $this->hasErrors = true;
                $errorCount++;
            }
        }

        if (!$errorCount) {
            $this->log($file . ': No syntax errors detected', $this->logLevel);

            if ($this->cache) {
                $this->cache->put($file, filemtime($file));
            }
        }
    }
}
<?php

/**
 * $Id: a2989958865651cca9c94d8b17c5ecf041f78614 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/PhingFile.php';

/**
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: a2989958865651cca9c94d8b17c5ecf041f78614 $
 * @package phing.tasks.ext.phploc
 */
abstract class AbstractPHPLocFormatter
{
    /**
     * @param array $count
     * @param bool $countTests
     * @return mixed
     */
    abstract public function printResult(array $count, $countTests = false);

    /**
     * @var bool
     */
    protected $useFile = true;

    /**
     * @var string
     */
    protected $toDir = ".";

    /**
     * @var string
     */
    protected $outfile = "";

    /**
     * Sets whether to store formatting results in a file
     * @param $useFile
     */
    public function setUseFile($useFile)
    {
        $this->useFile = $useFile;
    }

    /**
     * Returns whether to store formatting results in a file
     */
    public function getUseFile()
    {
        return $this->useFile;
    }

    /**
     * Sets output directory
     * @param string $toDir
     */
    public function setToDir($toDir)
    {
        if (!is_dir($toDir)) {
            $toDir = new PhingFile($toDir);
            $toDir->mkdirs();
        }

        $this->toDir = $toDir;
    }

    /**
     * Returns output directory
     * @return string
     */
    public function getToDir()
    {
        return $this->toDir;
    }

    /**
     * Sets output filename
     * @param string $outfile
     */
    public function setOutfile($outfile)
    {
        $this->outfile = $outfile;
    }

    /**
     * Returns output filename
     * @return string
     */
    public function getOutfile()
    {
        return $this->outfile;
    }

}<?php
/**
 * $Id: d7cc7bea3ec2cc98a53da57434dc778d1697f565 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/phploc/AbstractPHPLocFormatter.php';

/**
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: d7cc7bea3ec2cc98a53da57434dc778d1697f565 $
 * @package phing.tasks.ext.phploc
 */
class PHPLocCSVFormatter extends AbstractPHPLocFormatter
{
    public function printResult(array $count, $countTests = false)
    {
        $printerClass = '\\SebastianBergmann\\PHPLOC\\Log\\CSV\\Single';
        $printer = new $printerClass();
        $printer->printResult($this->getToDir() . DIRECTORY_SEPARATOR . $this->getOutfile(), $count);
    }
}
<?php
/**
 * $Id: 5911dc47d5037c87f6c0c11d815c05361cad9bc0 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 5911dc47d5037c87f6c0c11d815c05361cad9bc0 $
 * @package phing.tasks.ext.phploc
 */
class PHPLocFormatterElement
{
    /**
     * @var string
     */
    protected $type = "";

    /**
     * @var bool
     */
    protected $useFile = true;

    /**
     * @var string
     */
    protected $toDir = ".";

    /**
     * @var string
     */
    protected $outfile = "phploc-report";

    /**
     * Loads a specific formatter type
     * @param string $type
     */
    public function setType($type)
    {
        $this->type = $type;
    }

    /**
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Sets whether to store formatting results in a file
     * @param $useFile
     */
    public function setUseFile($useFile)
    {
        $this->useFile = $useFile;
    }

    /**
     * Returns whether to store formatting results in a file
     */
    public function getUseFile()
    {
        return $this->useFile;
    }

    /**
     * Sets output directory
     * @param string $toDir
     */
    public function setToDir($toDir)
    {
        $this->toDir = $toDir;
    }

    /**
     * Returns output directory
     * @return string
     */
    public function getToDir()
    {
        return $this->toDir;
    }

    /**
     * Sets output filename
     * @param string $outfile
     */
    public function setOutfile($outfile)
    {
        $this->outfile = $outfile;
    }

    /**
     * Returns output filename
     * @return string
     */
    public function getOutfile()
    {
        return $this->outfile;
    }
}
<?php

/**
 * $Id: df866a6b5cdfae3f31e201fe05789f9616db2059 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/phploc/AbstractPHPLocFormatter.php';

/**
 * A wrapper for the implementations of PHPUnit2ResultFormatter.
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: df866a6b5cdfae3f31e201fe05789f9616db2059 $
 * @package phing.tasks.ext.phploc
 */
class PHPLocFormatterFactory
{
    /**
     * Returns formatter object
     * @param PHPLocFormatterElement $formatterElement
     * @throws BuildException
     * @return AbstractPHPLocFormatter
     */
    public static function createFormatter($formatterElement)
    {
        $formatter = null;
        $type = $formatterElement->getType();

        switch ($type) {
            case "xml":
                require_once 'phing/tasks/ext/phploc/PHPLocXMLFormatter.php';
                $formatter = new PHPLocXMLFormatter();
                break;
            case "csv":
                require_once 'phing/tasks/ext/phploc/PHPLocCSVFormatter.php';
                $formatter = new PHPLocCSVFormatter();
                break;
            case "txt":
            case "cli":
                require_once 'phing/tasks/ext/phploc/PHPLocTextFormatter.php';
                $formatter = new PHPLocTextFormatter();
                break;
            default:
                throw new BuildException("Formatter '" . $type . "' not implemented");
        }

        $formatter->setOutfile($formatterElement->getOutfile());
        $formatter->setToDir($formatterElement->getToDir());
        $formatter->setUseFile($formatterElement->getUseFile());

        return $formatter;
    }
}
<?php
/**
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/BuildException.php';
require_once 'phing/tasks/ext/phploc/PHPLocFormatterElement.php';
require_once 'phing/tasks/ext/phploc/PHPLocFormatterFactory.php';

/**
 * Runs phploc a tool for quickly measuring the size of PHP projects.
 *
 * @package phing.tasks.ext.phploc
 * @author  Raphael Stolt <raphael.stolt@gmail.com>
 */
class PHPLocTask extends Task
{
    /**
     * @var array
     */
    protected $suffixesToCheck = array('php');

    /**
     * @var array
     */
    protected $acceptedReportTypes = array('cli', 'txt', 'xml', 'csv');

    /**
     * @var null
     */
    protected $reportDirectory = null;

    /**
     * @var string
     */
    protected $reportType = 'cli';

    /**
     * @var string
     */
    protected $reportFileName = 'phploc-report';

    /**
     * @var bool
     */
    protected $countTests = false;

    /**
     * @var null|PhingFile
     */
    protected $fileToCheck = null;

    /**
     * @var array
     */
    protected $filesToCheck = array();

    /**
     * @var FileSet[]
     */
    protected $fileSets = array();

    /**
     * @var PHPLocFormatterElement[]
     */
    protected $formatterElements = array();

    /**
     * @var string
     */
    private $pharLocation = "";

    /**
     * @param string $suffixListOrSingleSuffix
     */
    public function setSuffixes($suffixListOrSingleSuffix)
    {
        if (stripos($suffixListOrSingleSuffix, ',')) {
            $suffixes = explode(',', $suffixListOrSingleSuffix);
            $this->suffixesToCheck = array_map('trim', $suffixes);
        } else {
            $this->suffixesToCheck[] = trim($suffixListOrSingleSuffix);
        }
    }

    /**
     * @param PhingFile $file
     */
    public function setFile(PhingFile $file)
    {
        $this->fileToCheck = trim($file);
    }

    /**
     * @param boolean $countTests
     */
    public function setCountTests($countTests)
    {
        $this->countTests = StringHelper::booleanValue($countTests);
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->fileSets[] = $fs;
    }

    /**
     * @param string $type
     */
    public function setReportType($type)
    {
        $this->reportType = trim($type);
    }

    /**
     * @param string $name
     */
    public function setReportName($name)
    {
        $this->reportFileName = trim($name);
    }

    /**
     * @param string $directory
     */
    public function setReportDirectory($directory)
    {
        $this->reportDirectory = trim($directory);
    }

    /**
     * @param string $pharLocation
     */
    public function setPharLocation($pharLocation)
    {
        $this->pharLocation = $pharLocation;
    }

    /**
     * @param PHPLocFormatterElement $formatterElement
     */
    public function addFormatter(PHPLocFormatterElement $formatterElement)
    {
        $this->formatterElements[] = $formatterElement;
    }

    /**
     * @throws BuildException
     */
    protected function loadDependencies()
    {
        if (!empty($this->pharLocation)) {
            // hack to prevent PHPLOC from starting in CLI mode and halting Phing
            eval("namespace SebastianBergmann\PHPLOC\CLI;
class Application
{
    public function run() {}
}");

            ob_start();
            include $this->pharLocation;
            ob_end_clean();
        }

        if (!class_exists('\SebastianBergmann\PHPLOC\Analyser')) {
            if (!@include_once 'SebastianBergmann/PHPLOC/autoload.php') {
                throw new BuildException(
                    'PHPLocTask depends on PHPLoc being installed and on include_path.',
                    $this->getLocation()
                );
            }
        }
    }

    public function main()
    {
        $this->loadDependencies();

        $this->validateProperties();

        if (count($this->fileSets) > 0) {
            foreach ($this->fileSets as $fileSet) {
                $directoryScanner = $fileSet->getDirectoryScanner($this->project);
                $files = $directoryScanner->getIncludedFiles();
                $directory = $fileSet->getDir($this->project)->getPath();

                foreach ($files as $file) {
                    if ($this->isFileSuffixSet($file)) {
                        $this->filesToCheck[] = $directory . DIRECTORY_SEPARATOR . $file;
                    }
                }
            }

            $this->filesToCheck = array_unique($this->filesToCheck);
        }

        $this->runPhpLocCheck();
    }

    /**
     * @throws BuildException
     */
    private function validateProperties()
    {
        if ($this->fileToCheck === null && count($this->fileSets) === 0) {
            throw new BuildException('Missing either a nested fileset or the attribute "file" set.');
        }

        if ($this->fileToCheck !== null) {
            if (!file_exists($this->fileToCheck)) {
                throw new BuildException("File to check doesn't exist.");
            }

            if (!$this->isFileSuffixSet($this->fileToCheck)) {
                throw new BuildException('Suffix of file to check is not defined in "suffixes" attribute.');
            }

            if (count($this->fileSets) > 0) {
                throw new BuildException('Either use a nested fileset or "file" attribute; not both.');
            }
        }

        if (count($this->suffixesToCheck) === 0) {
            throw new BuildException('No file suffix defined.');
        }

        if (count($this->formatterElements) == 0) {
            if ($this->reportType === null) {
                throw new BuildException('No report type or formatters defined.');
            }

            if ($this->reportType !== null && !in_array($this->reportType, $this->acceptedReportTypes)) {
                throw new BuildException('Unaccepted report type defined.');
            }

            if ($this->reportType !== 'cli' && $this->reportDirectory === null) {
                throw new BuildException('No report output directory defined.');
            }

            if ($this->reportDirectory !== null && !is_dir($this->reportDirectory)) {
                $reportOutputDir = new PhingFile($this->reportDirectory);

                $logMessage = "Report output directory doesn't exist, creating: "
                    . $reportOutputDir->getAbsolutePath() . '.';

                $this->log($logMessage);
                $reportOutputDir->mkdirs();
            }

            if ($this->reportType !== 'cli') {
                $this->reportFileName .= '.' . $this->reportType;
            }

            $formatterElement = new PHPLocFormatterElement();
            $formatterElement->setType($this->reportType);
            $formatterElement->setUseFile($this->reportDirectory !== null);
            $formatterElement->setToDir($this->reportDirectory);
            $formatterElement->setOutfile($this->reportFileName);
            $this->formatterElements[] = $formatterElement;
        }
    }

    /**
     * @param string $filename
     *
     * @return boolean
     */
    protected function isFileSuffixSet($filename)
    {
        return in_array(pathinfo($filename, PATHINFO_EXTENSION), $this->suffixesToCheck);
    }

    protected function runPhpLocCheck()
    {
        $files = $this->getFilesToCheck();
        $count = $this->getCountForFiles($files);

        foreach ($this->formatterElements as $formatterElement) {
            $formatter = PHPLocFormatterFactory::createFormatter($formatterElement);

            if ($formatterElement->getType() != 'cli') {
                $logMessage = 'Writing report to: '
                    . $formatterElement->getToDir() . DIRECTORY_SEPARATOR . $formatterElement->getOutfile();

                $this->log($logMessage);
            }

            $formatter->printResult($count, $this->countTests);
        }
    }

    /**
     * @return SplFileInfo[]
     */
    protected function getFilesToCheck()
    {
        $files = array();

        if (count($this->filesToCheck) > 0) {
            foreach ($this->filesToCheck as $file) {
                $files[] = new SplFileInfo($file);
            }
        } elseif ($this->fileToCheck !== null) {
            $files = array(new SplFileInfo($this->fileToCheck));
        }

        return $files;
    }

    /**
     * @param SplFileInfo[] $files
     *
     * @return array
     */
    protected function getCountForFiles(array $files)
    {
        $analyserClass = '\\SebastianBergmann\\PHPLOC\\Analyser';
        $analyser = new $analyserClass();

        return $analyser->countFiles($files, $this->countTests);
    }
}
<?php
/**
 * $Id: d9e8d9450dcecf7f3691236655b802512460618b $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/phploc/AbstractPHPLocFormatter.php';

/**
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: d9e8d9450dcecf7f3691236655b802512460618b $
 * @package phing.tasks.ext.phploc
 */
class PHPLocTextFormatter extends AbstractPHPLocFormatter
{
    public function printResult(array $count, $countTests = false)
    {
        if ($this->getUseFile()) {
            $outputClass = '\\Symfony\\Component\\Console\\Output\\StreamOutput';
            $stream = fopen($this->getToDir() . DIRECTORY_SEPARATOR . $this->getOutfile(), 'a+');
            $output = new $outputClass($stream);
        } else {
            $outputClass = '\\Symfony\\Component\\Console\\Output\\ConsoleOutput';
            $output = new $outputClass();
        }

        $printerClass = '\\SebastianBergmann\\PHPLOC\\Log\\Text';
        $printer = new $printerClass();
        $printer->printResult($output, $count, $countTests);
    }
}
<?php
/**
 * $Id: 11510e1b71f4f3a91df7fd8ce39ed5d24615d991 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/phploc/AbstractPHPLocFormatter.php';

/**
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 11510e1b71f4f3a91df7fd8ce39ed5d24615d991 $
 * @package phing.tasks.ext.phploc
 */
class PHPLocXMLFormatter extends AbstractPHPLocFormatter
{
    public function printResult(array $count, $countTests = false)
    {
        $printerClass = '\\SebastianBergmann\\PHPLOC\\Log\\XML';
        $printer = new $printerClass();
        $printer->printResult($this->getToDir() . DIRECTORY_SEPARATOR . $this->getOutfile(), $count);
    }
}
<?php
/**
 * $Id: fa23de508582a5bc1d36fab6153d64df333099c6 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/PhingFile.php';

/**
 * A wrapper for the implementations of PHPMDResultFormatter.
 *
 * @package phing.tasks.ext.phpmd
 * @author  Benjamin Schultz <bschultz@proqrent.de>
 * @version $Id: fa23de508582a5bc1d36fab6153d64df333099c6 $
 * @since   2.4.1
 */
class PHPMDFormatterElement
{
    /**
     * @var PHPMDResultFormatter
     */
    protected $formatter = null;

    /**
     * The type of the formatter.
     *
     * @var string
     */
    protected $type = "";

    /**
     * @var string
     */
    protected $className = "";

    /**
     * Whether to use file (or write output to phing log).
     *
     * @var boolean
     */
    protected $useFile = true;

    /**
     * Output file for formatter.
     *
     * @var PhingFile
     */
    protected $outfile = null;

    /**
     * Sets the formatter type.
     *
     * @param string $type Type of the formatter
     *
     * @throws BuildException
     */
    public function setType($type)
    {
        $this->type = $type;
        switch ($this->type) {
            case 'xml':
                $this->className = 'XMLRenderer';
                break;

            case 'html':
                $this->className = 'HTMLRenderer';
                break;

            case 'text':
                $this->className = 'TextRenderer';
                break;

            default:
                throw new BuildException('Formatter "' . $this->type . '" not implemented');
        }
    }

    /**
     * Get the formatter type
     *
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Set whether to write formatter results to file or not.
     *
     * @param boolean $useFile True or false.
     */
    public function setUseFile($useFile)
    {
        $this->useFile = StringHelper::booleanValue($useFile);
    }

    /**
     * Return whether to write formatter results to file or not.
     *
     * @return boolean
     */
    public function getUseFile()
    {
        return $this->useFile;
    }

    /**
     * Sets the output file for the formatter results.
     *
     * @param PhingFile $outfile The output file
     */
    public function setOutfile(PhingFile $outfile)
    {
        $this->outfile = $outfile;
    }

    /**
     * Get the output file.
     *
     * @return PhingFile
     */
    public function getOutfile()
    {
        return $this->outfile;
    }

    /**
     * Creates a report renderer instance based on the formatter type.
     *
     * @return PHP_PMD_AbstractRenderer
     * @throws BuildException           When the specified renderer does not exist.
     */
    public function getRenderer()
    {
        if (!class_exists('\\PHPMD\\Writer\\StreamWriter')) {
            $renderClass = 'PHP_PMD_RENDERER_' . $this->className;
            $writerClass = 'PHP_PMD_Writer_Stream';
            include_once 'PHP/PMD/Renderer/' . $this->className . '.php';
            include_once 'PHP/PMD/Writer/Stream.php';
        } else {
            $renderClass = 'PHPMD\Renderer\\' . $this->className;
            $writerClass = '\PHPMD\Writer\StreamWriter';
        }

        $renderer = new $renderClass();

        // Create a report stream
        if ($this->getUseFile() === false || $this->getOutfile() === null) {
            $stream = STDOUT;
        } else {
            $stream = fopen($this->getOutfile()->getAbsoluteFile(), 'wb');
        }

        $renderer->setWriter(new $writerClass($stream));

        return $renderer;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/util/DataStore.php';

use PHPMD\AbstractRenderer;
use PHPMD\Report;

/**
 * This class will remove files with violations from cache
 *
 * @category  PHP
 * @package   PHPMD
 * @author    Rui Filipe Da Cunha Alves <ruifil@ruifil.com>
 */
class PHPMDRendererRemoveFromCache extends AbstractRenderer
{
    /**
     * Cache data storage
     * @var DataStore
     */
    protected $cache;

    /**
     * Constructor
     *
     * @param DataStore $cache
     */
    public function __construct($cache)
    {
        $this->cache = $cache;
    }

    /**
     * This method will be called when the engine has finished the source
     * analysis phase. To remove file with violations from cache.
     *
     * @param Report $report
     * @return void
     */
    public function renderReport(Report $report)
    {
        foreach ($report->getRuleViolations() as $violation) {
            $fileName = $violation->getFileName();
            $this->cache->remove($fileName, null);
        }
    }
}
<?php
/**
 *  $Id: 6de5c80e97545cd52494c53490e96c09aa61e703 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/phpmd/PHPMDFormatterElement.php';

/**
 * Runs PHP Mess Detector. Checking PHP files for several potential problems
 * based on rulesets.
 *
 * @package phing.tasks.ext.phpmd
 * @author  Benjamin Schultz <bschultz@proqrent.de>
 * @version $Id: 6de5c80e97545cd52494c53490e96c09aa61e703 $
 * @since   2.4.1
 */
class PHPMDTask extends Task
{
    /**
     * A php source code filename or directory
     *
     * @var PhingFile
     */
    protected $file = null;

    /**
     * All fileset objects assigned to this task
     *
     * @var FileSet[]
     */
    protected $filesets = array();

    /**
     * The rule-set filenames or identifier.
     *
     * @var string
     */
    protected $rulesets = 'codesize,unusedcode';

    /**
     * The minimum priority for rules to load.
     *
     * @var integer
     */
    protected $minimumPriority = 0;

    /**
     * List of valid file extensions for analyzed files.
     *
     * @var array
     */
    protected $allowedFileExtensions = array('php');

    /**
     * List of exclude directory patterns.
     *
     * @var array
     */
    protected $ignorePatterns = array('.git', '.svn', 'CVS', '.bzr', '.hg');

    /**
     * The format for the report
     *
     * @var string
     */
    protected $format = 'text';

    /**
     * Formatter elements.
     *
     * @var PHPMDFormatterElement[]
     */
    protected $formatters = array();

    /**
     * @var bool
     */
    protected $newVersion = true;

    /**
     * @var string
     */
    protected $pharLocation = "";

    /**
     * Cache data storage
     *
     * @var DataStore
     */
    protected $cache;

    /**
     * Set the input source file or directory.
     *
     * @param PhingFile $file The input source file or directory.
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Sets the minimum rule priority.
     *
     * @param integer $minimumPriority Minimum rule priority.
     */
    public function setMinimumPriority($minimumPriority)
    {
        $this->minimumPriority = $minimumPriority;
    }

    /**
     * Sets the rule-sets.
     *
     * @param string $ruleSetFileNames Comma-separated string of rule-set filenames or identifier.
     */
    public function setRulesets($ruleSetFileNames)
    {
        $this->rulesets = $ruleSetFileNames;
    }

    /**
     * Sets a list of filename extensions for valid php source code files.
     *
     * @param string $fileExtensions List of valid file extensions without leading dot.
     */
    public function setAllowedFileExtensions($fileExtensions)
    {
        $this->allowedFileExtensions = array();

        $token = ' ,;';
        $ext = strtok($fileExtensions, $token);

        while ($ext !== false) {
            $this->allowedFileExtensions[] = $ext;
            $ext = strtok($token);
        }
    }

    /**
     * Sets a list of ignore patterns that is used to exclude directories from the source analysis.
     *
     * @param string $ignorePatterns List of ignore patterns.
     */
    public function setIgnorePatterns($ignorePatterns)
    {
        $this->ignorePatterns = array();

        $token = ' ,;';
        $pattern = strtok($ignorePatterns, $token);

        while ($pattern !== false) {
            $this->ignorePatterns[] = $pattern;
            $pattern = strtok($token);
        }
    }

    /**
     * Create object for nested formatter element.
     *
     * @return PHPMDFormatterElement
     */
    public function createFormatter()
    {
        $num = array_push($this->formatters, new PHPMDFormatterElement());

        return $this->formatters[$num - 1];
    }

    /**
     * @param string $format
     */
    public function setFormat($format)
    {
        $this->format = $format;
    }

    /**
     * @param string $pharLocation
     */
    public function setPharLocation($pharLocation)
    {
        $this->pharLocation = $pharLocation;
    }

    /**
     * Whether to store last-modified times in cache
     *
     * @param PhingFile $file
     */
    public function setCacheFile(PhingFile $file)
    {
        $this->cache = new DataStore($file);
    }

    /**
     * Find PHPMD
     *
     * @return string
     * @throws BuildException
     */
    protected function loadDependencies()
    {
        if (!empty($this->pharLocation)) {
            include_once 'phar://' . $this->pharLocation . '/vendor/autoload.php';
        }

        $className = '\PHPMD\PHPMD';

        if (!class_exists($className)) {
            @include_once 'PHP/PMD.php';
            $className = "PHP_PMD";
            $this->newVersion = false;
        }

        if (!class_exists($className)) {
            throw new BuildException(
                'PHPMDTask depends on PHPMD being installed and on include_path or listed in pharLocation.',
                $this->getLocation()
            );
        }

        if ($this->newVersion) {
            //weird syntax to allow 5.2 parser compatibility
            $minPriority = constant('\PHPMD\AbstractRule::LOWEST_PRIORITY');
            require_once 'phing/tasks/ext/phpmd/PHPMDRendererRemoveFromCache.php';
        } else {
            require_once 'PHP/PMD/AbstractRule.php';
            $minPriority = PHP_PMD_AbstractRule::LOWEST_PRIORITY;
        }

        if (!$this->minimumPriority) {
            $this->minimumPriority = $minPriority;
        }

        return $className;
    }

    /**
     * Return the list of files to parse
     *
     * @return string[] list of absolute files to parse
     */
    protected function getFilesToParse()
    {
        $filesToParse = array();

        if ($this->file instanceof PhingFile) {
            $filesToParse[] = $this->file->getPath();
        } else {
            // append any files in filesets
            foreach ($this->filesets as $fs) {
                $dir = $fs->getDir($this->project)->getAbsolutePath();
                foreach ($fs->getDirectoryScanner($this->project)->getIncludedFiles() as $filename) {
                    $fileAbsolutePath = $dir . DIRECTORY_SEPARATOR . $filename;
                    if ($this->cache) {
                        $lastMTime = $this->cache->get($fileAbsolutePath);
                        $currentMTime = filemtime($fileAbsolutePath);
                        if ($lastMTime >= $currentMTime) {
                            continue;
                        } else {
                            $this->cache->put($fileAbsolutePath, $currentMTime);
                        }
                    }
                    $filesToParse[] = $fileAbsolutePath;
                }
            }
        }
        return $filesToParse;
    }

    /**
     * Executes PHPMD against PhingFile or a FileSet
     *
     * @throws BuildException - if the phpmd classes can't be loaded.
     */
    public function main()
    {
        $className = $this->loadDependencies();

        if (!isset($this->file) and count($this->filesets) == 0) {
            throw new BuildException('Missing either a nested fileset or attribute "file" set');
        }

        if (count($this->formatters) == 0) {
            // turn legacy format attribute into formatter
            $fmt = new PHPMDFormatterElement();
            $fmt->setType($this->format);
            $fmt->setUseFile(false);

            $this->formatters[] = $fmt;
        }

        $reportRenderers = array();

        foreach ($this->formatters as $fe) {
            if ($fe->getType() == '') {
                throw new BuildException('Formatter missing required "type" attribute.');
            }

            if ($fe->getUsefile() && $fe->getOutfile() === null) {
                throw new BuildException('Formatter requires "outfile" attribute when "useFile" is true.');
            }

            $reportRenderers[] = $fe->getRenderer();
        }

        if ($this->newVersion && $this->cache) {
            $reportRenderers[] = new PHPMDRendererRemoveFromCache($this->cache);
        } else {
            $this->cache = null; // cache not compatible to old version
        }

        // Create a rule set factory
        if ($this->newVersion) {
            $ruleSetClass = '\PHPMD\RuleSetFactory';
            $ruleSetFactory = new $ruleSetClass(); //php 5.2 parser compatibility

        } else {
            if (!class_exists("PHP_PMD_RuleSetFactory")) {
                    @include 'PHP/PMD/RuleSetFactory.php';
            }
            $ruleSetFactory = new PHP_PMD_RuleSetFactory();
        }
        $ruleSetFactory->setMinimumPriority($this->minimumPriority);

        /**
         * @var PHPMD\PHPMD $phpmd
         */
        $phpmd = new $className();
        $phpmd->setFileExtensions($this->allowedFileExtensions);
        $phpmd->setIgnorePattern($this->ignorePatterns);

        $filesToParse = $this->getFilesToParse();

        if (count($filesToParse) > 0) {
            $inputPath = implode(',', $filesToParse);

            $this->log('Processing files...');

            $phpmd->processFiles($inputPath, $this->rulesets, $reportRenderers, $ruleSetFactory);

            if ($this->cache) {
                $this->cache->commit();
            }

            $this->log('Finished processing files');
        } else {
            $this->log('No files to process');
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/phpunit/PHPUnitUtil.php';
require_once 'phing/types/FileSet.php';

/**
 * Scans a list of files given by the fileset attribute, extracts valid test cases
 *
 * @author Michiel Rook <mrook@php.net>
 *
 * @package phing.tasks.ext.phpunit
 *
 * @since 2.1.0
 */
class BatchTest
{
    /**
     * The list of filesets containing the testcase filename rules.
     *
     * @var array $filesets
     */
    private $filesets = array();

    /** the reference to the project */
    private $project = null;

    /** the classpath to use with Phing::__import() calls */
    private $classpath = null;

    /** names of classes to exclude */
    private $excludeClasses = array();

    /** name of the batchtest/suite */
    protected $name = "Phing Batchtest";

    /**
     * Create a new batchtest instance
     *
     * @param Project the project it depends on.
     */
    public function __construct(Project $project)
    {
        $this->project = $project;
    }

    /**
     * Sets the name of the batchtest/suite
     * @param $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Sets the classes to exclude.
     *
     * @param string $exclude
     *
     * @return void
     */
    public function setExclude($exclude)
    {
        $this->excludeClasses = explode(" ", $exclude);
    }

    /**
     * Sets the classpath.
     *
     * @param Path $classpath
     *
     * @return void
     */
    public function setClasspath(Path $classpath)
    {
        if ($this->classpath === null) {
            $this->classpath = $classpath;
        } else {
            $this->classpath->append($classpath);
        }
    }

    /**
     * Creates a new Path object.
     *
     * @return Path
     */
    public function createClasspath()
    {
        $this->classpath = new Path();

        return $this->classpath;
    }

    /**
     * Returns the classpath.
     *
     * @return Path
     */
    public function getClasspath()
    {
        return $this->classpath;
    }

    /**
     * Add a new fileset containing the XML results to aggregate.
     *
     * @param FileSet $fileset the new fileset containing XML results.
     *
     * @return void
     */
    public function addFileSet(FileSet $fileset)
    {
        $this->filesets[] = $fileset;
    }

    /**
     * Iterate over all filesets and return the filename of all files.
     *
     * @return array an array of filenames
     */
    private function getFilenames()
    {
        $filenames = array();

        foreach ($this->filesets as $fileset) {
            $ds = $fileset->getDirectoryScanner($this->project);
            $ds->scan();

            $files = $ds->getIncludedFiles();

            foreach ($files as $file) {
                $filenames[] = $ds->getBaseDir() . "/" . $file;
            }
        }

        return $filenames;
    }

    /**
     * Checks wheter $input is a PHPUnit Test.
     *
     * @param $input
     *
     * @return bool
     */
    private function isTestCase($input)
    {
        return is_subclass_of($input, 'PHPUnit_Framework_TestCase') || is_subclass_of(
            $input,
            'PHPUnit_Framework_TestSuite'
        );
    }

    /**
     * Filters an array of classes, removes all classes that are not test cases or test suites,
     * or classes that are declared abstract.
     *
     * @param object $input
     *
     * @return bool
     */
    private function filterTests($input)
    {
        $reflect = new ReflectionClass($input);

        return $this->isTestCase($input) && (!$reflect->isAbstract());
    }

    /**
     * Returns an array of test cases and test suites that are declared
     * by the files included by the filesets
     *
     * @return array an array of tests.
     */
    public function elements()
    {
        $filenames = $this->getFilenames();

        $declaredClasses = array();

        foreach ($filenames as $filename) {
            $definedClasses = PHPUnitUtil::getDefinedClasses($filename, $this->classpath);

            foreach ($definedClasses as $definedClass) {
                $this->project->log("(PHPUnit) Adding $definedClass (from $filename) to tests.", Project::MSG_DEBUG);
            }

            $declaredClasses = array_merge($declaredClasses, $definedClasses);
        }

        $elements = array_filter($declaredClasses, array($this, "filterTests"));

        return $elements;
    }
}
<?php
/**
 * $Id: 6ac25b4744ffc88118865a46b54b34923bd4b94a $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/phpunit/formatter/PHPUnitResultFormatter.php';

/**
 * Prints Clover XML output of the test
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 6ac25b4744ffc88118865a46b54b34923bd4b94a $
 * @package phing.tasks.ext.formatter
 * @since 2.4.0
 */
class CloverPHPUnitResultFormatter extends PHPUnitResultFormatter
{
    /**
     * @var PHPUnit_Framework_TestResult
     */
    private $result = null;

    /**
     * PHPUnit version
     * @var string
     */
    private $version = null;

    /**
     * @param PHPUnitTask $parentTask
     */
    public function __construct(PHPUnitTask $parentTask)
    {
        parent::__construct($parentTask);

        $this->version = PHPUnit_Runner_Version::id();
    }

    /**
     * @return string
     */
    public function getExtension()
    {
        return ".xml";
    }

    /**
     * @return string
     */
    public function getPreferredOutfile()
    {
        return "clover-coverage";
    }

    /**
     * @param PHPUnit_Framework_TestResult $result
     */
    public function processResult(PHPUnit_Framework_TestResult $result)
    {
        $this->result = $result;
    }

    public function endTestRun()
    {
        $coverage = $this->result->getCodeCoverage();

        if (!empty($coverage)) {
            if (class_exists('PHP_CodeCoverage_Report_Clover')) {
                $clover = new PHP_CodeCoverage_Report_Clover();
            } elseif (class_exists('\SebastianBergmann\CodeCoverage\Report\Clover')) {
                $cloverClass = '\SebastianBergmann\CodeCoverage\Report\Clover';
                $clover = new $cloverClass;
            }

            $contents = $clover->process($coverage);

            if ($this->out) {
                $this->out->write($contents);
                $this->out->close();
            }
        }

        parent::endTestRun();
    }
}
<?php
/**
 * $Id: 8264b2201e8667bf661e5fbe6ebe050413eb68d1 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */
require_once 'phing/tasks/ext/phpunit/formatter/PHPUnitResultFormatter.php';
/**
 * Prints Clover XML output of the test
 *
 * @author Daniel Kreckel <daniel@kreckel.koeln>
 * @version $Id: 8264b2201e8667bf661e5fbe6ebe050413eb68d1 $
 * @package phing.tasks.ext.formatter
 * @since 2.1.1
 */
class Crap4jPHPUnitResultFormatter extends PHPUnitResultFormatter
{
    /**
     * @var PHPUnit_Framework_TestResult
     */
    private $result = null;
    /**
     * PHPUnit version
     * @var string
     */
    private $version = null;
    /**
     * @param PHPUnitTask $parentTask
     */
    public function __construct(PHPUnitTask $parentTask)
    {
        parent::__construct($parentTask);
        $this->version = PHPUnit_Runner_Version::id();
    }
    /**
     * @return string
     */
    public function getExtension()
    {
        return ".xml";
    }
    /**
     * @return string
     */
    public function getPreferredOutfile()
    {
        return "crap4j-coverage";
    }
	
    /**
     * @param PHPUnit_Framework_TestResult $result
     */
    public function processResult(PHPUnit_Framework_TestResult $result)
    {
        $this->result = $result;
    }
	
    public function endTestRun()
    {
        $coverage = $this->result->getCodeCoverage();
        if (!empty($coverage)) {
            if (class_exists('PHP_CodeCoverage_Report_Crap4j')) {
                $crap = new PHP_CodeCoverage_Report_Crap4j();
            } elseif (class_exists('\SebastianBergmann\CodeCoverage\Report\Crap4j')) {
                $crapClass = '\SebastianBergmann\CodeCoverage\Report\Crap4j';
                $crap = new $crapClass();
            }
            $contents = $crap->process($coverage);
            if ($this->out) {
                $this->out->write($contents);
                $this->out->close();
            }
        }
        parent::endTestRun();
    }
}
<?php
/**
 * $Id: e6377299d86c4e8c4e4eb5875b346d44baf9fe25 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/Writer.php';

/**
 * This abstract class describes classes that format the results of a PHPUnit testrun.
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: e6377299d86c4e8c4e4eb5875b346d44baf9fe25 $
 * @package phing.tasks.ext.phpunit.formatter
 * @since 2.1.0
 */
abstract class PHPUnitResultFormatter implements PHPUnit_Framework_TestListener
{
    protected $out = null;

    protected $project = null;

    /** @var bool|array */
    private $timers = false;

    /** @var bool|array */
    private $runCounts = false;

    /** @var bool|array */
    private $failureCounts = false;

    /** @var bool|array */
    private $errorCounts = false;

    /** @var bool|array */
    private $incompleteCounts = false;

    /** @var bool|array */
    private $skipCounts = false;

    /**
     * Constructor
     * @param PHPUnitTask $parentTask Calling Task
     */
    public function __construct(PHPUnitTask $parentTask)
    {
        $this->project = $parentTask->getProject();
    }

    /**
     * Sets the writer the formatter is supposed to write its results to.
     * @param Writer $out
     */
    public function setOutput(Writer $out)
    {
        $this->out = $out;
    }

    /**
     * Returns the extension used for this formatter
     *
     * @return string the extension
     */
    public function getExtension()
    {
        return "";
    }

    /**
     * @return string
     */
    public function getPreferredOutfile()
    {
        return "";
    }

    /**
     * @param PHPUnit_Framework_TestResult $result
     */
    public function processResult(PHPUnit_Framework_TestResult $result)
    {
    }

    public function startTestRun()
    {
        $this->timers = array($this->getMicrotime());
        $this->runCounts = array(0);
        $this->failureCounts = array(0);
        $this->errorCounts = array(0);
        $this->incompleteCounts = array(0);
        $this->skipCounts = array(0);
    }

    public function endTestRun()
    {
    }

    /**
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        $this->timers[] = $this->getMicrotime();
        $this->runCounts[] = 0;
        $this->failureCounts[] = 0;
        $this->errorCounts[] = 0;
        $this->incompleteCounts[] = 0;
        $this->skipCounts[] = 0;
    }

    /**
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        $lastRunCount = array_pop($this->runCounts);
        $this->runCounts[count($this->runCounts) - 1] += $lastRunCount;

        $lastFailureCount = array_pop($this->failureCounts);
        $this->failureCounts[count($this->failureCounts) - 1] += $lastFailureCount;

        $lastErrorCount = array_pop($this->errorCounts);
        $this->errorCounts[count($this->errorCounts) - 1] += $lastErrorCount;

        $lastIncompleteCount = array_pop($this->incompleteCounts);
        $this->incompleteCounts[count($this->incompleteCounts) - 1] += $lastIncompleteCount;

        $lastSkipCount = array_pop($this->skipCounts);
        $this->skipCounts[count($this->skipCounts) - 1] += $lastSkipCount;

        array_pop($this->timers);
    }

    /**
     * @param PHPUnit_Framework_Test $test
     */
    public function startTest(PHPUnit_Framework_Test $test)
    {
        $this->runCounts[count($this->runCounts) - 1]++;
    }

    /**
     * @param PHPUnit_Framework_Test $test
     * @param float $time
     */
    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
    }

    /**
     * @param PHPUnit_Framework_Test $test
     * @param Exception $e
     * @param float $time
     */
    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->errorCounts[count($this->errorCounts) - 1]++;
    }

    /**
     * @param PHPUnit_Framework_Test $test
     * @param PHPUnit_Framework_AssertionFailedError $e
     * @param float $time
     */
    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
    {
        $this->failureCounts[count($this->failureCounts) - 1]++;
    }

    /**
     * @param PHPUnit_Framework_Test $test
     * @param Exception $e
     * @param float $time
     */
    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->incompleteCounts[count($this->incompleteCounts) - 1]++;
    }

    /**
     * @param PHPUnit_Framework_Test $test
     * @param Exception $e
     * @param float $time
     */
    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->skipCounts[count($this->skipCounts) - 1]++;
    }

    /**
     * @param PHPUnit_Framework_Test $test
     * @param Exception $e
     * @param float $time
     */
    public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
    }

    /**
     * @return mixed
     */
    public function getRunCount()
    {
        return end($this->runCounts);
    }

    /**
     * @return mixed
     */
    public function getFailureCount()
    {
        return end($this->failureCounts);
    }

    /**
     * @return mixed
     */
    public function getErrorCount()
    {
        return end($this->errorCounts);
    }

    /**
     * @return mixed
     */
    public function getIncompleteCount()
    {
        return end($this->incompleteCounts);
    }

    /**
     * @return mixed
     */
    public function getSkippedCount()
    {
        return end($this->skipCounts);
    }

    /**
     * @return float|int
     */
    public function getElapsedTime()
    {
        if (end($this->timers)) {
            return $this->getMicrotime() - end($this->timers);
        } else {
            return 0;
        }
    }

    /**
     * @return float
     */
    private function getMicrotime()
    {
        list($usec, $sec) = explode(' ', microtime());

        return (float) $usec + (float) $sec;
    }
}
<?php
/**
 * $Id: 1568fd6d1603e5fa348b7d427e92f9c7c547f252 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/phpunit/formatter/PHPUnitResultFormatter.php';

/**
 * Prints plain text output of the test to a specified Writer.
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 1568fd6d1603e5fa348b7d427e92f9c7c547f252 $
 * @package phing.tasks.ext.phpunit.formatter
 * @since 2.1.0
 */
class PlainPHPUnitResultFormatter extends PHPUnitResultFormatter
{
    private $inner = "";

    /**
     * @return string
     */
    public function getExtension()
    {
        return ".txt";
    }

    /**
     * @return string
     */
    public function getPreferredOutfile()
    {
        return "testresults";
    }

    /**
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        parent::startTestSuite($suite);

        $this->inner = "";
    }

    /**
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        if ($suite->getName() == 'AllTests') {
            return false;
        }

        $sb = "Testsuite: " . $suite->getName() . "\n";
        $sb .= "Tests run: " . $this->getRunCount();
        $sb .= ", Failures: " . $this->getFailureCount();
        $sb .= ", Errors: " . $this->getErrorCount();
        $sb .= ", Incomplete: " . $this->getIncompleteCount();
        $sb .= ", Skipped: " . $this->getSkippedCount();
        $sb .= ", Time elapsed: " . sprintf('%0.5f', $this->getElapsedTime()) . " s\n";

        if ($this->out != null) {
            $this->out->write($sb);
            $this->out->write($this->inner);
        }

        parent::endTestSuite($suite);
    }

    /**
     * @param PHPUnit_Framework_Test $test
     * @param Exception $e
     * @param float $time
     */
    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        parent::addError($test, $e, $time);

        $this->formatError("ERROR", $test, $e);
    }

    /**
     * @param PHPUnit_Framework_Test $test
     * @param PHPUnit_Framework_AssertionFailedError $e
     * @param float $time
     */
    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
    {
        parent::addFailure($test, $e, $time);
        $this->formatError("FAILED", $test, $e);
    }

    /**
     * @param PHPUnit_Framework_Test $test
     * @param Exception $e
     * @param float $time
     */
    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        parent::addIncompleteTest($test, $e, $time);

        $this->formatError("INCOMPLETE", $test);
    }

    /**
     * @param PHPUnit_Framework_Test $test
     * @param Exception $e
     * @param float $time
     */
    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        parent::addSkippedTest($test, $e, $time);
        $this->formatError("SKIPPED", $test);
    }

    /**
     * @param $type
     * @param PHPUnit_Framework_Test $test
     * @param Exception $e
     */
    private function formatError($type, PHPUnit_Framework_Test $test, Exception $e = null)
    {
        if ($test != null) {
            $this->endTest($test, time());
        }

        $this->inner .= $test->getName() . " " . $type . "\n";

        if ($e !== null) {
            $this->inner .= $e->getMessage() . "\n";
            // $this->inner.= PHPUnit_Util_Filter::getFilteredStackTrace($e, true) . "\n";
        }
    }

    public function endTestRun()
    {
        parent::endTestRun();

        if ($this->out != null) {
            $this->out->close();
        }
    }
}
<?php
/**
 * $Id: f7d5ada145387c074b1e83a0a15bd56f089bcb54 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/phpunit/formatter/PHPUnitResultFormatter.php';

/**
 * Prints short summary output of the test to Phing's logging system.
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: f7d5ada145387c074b1e83a0a15bd56f089bcb54 $
 * @package phing.tasks.ext.formatter
 * @since 2.1.0
 */
class SummaryPHPUnitResultFormatter extends PHPUnitResultFormatter
{
    /**
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        parent::endTestSuite($suite);
    }

    public function endTestRun()
    {
        parent::endTestRun();

        $sb = "Total tests run: " . $this->getRunCount();
        $sb .= ", Failures: " . $this->getFailureCount();
        $sb .= ", Errors: " . $this->getErrorCount();
        $sb .= ", Incomplete: " . $this->getIncompleteCount();
        $sb .= ", Skipped: " . $this->getSkippedCount();
        $sb .= ", Time elapsed: " . sprintf('%0.5f', $this->getElapsedTime()) . " s\n";

        if ($this->out != null) {
            $this->out->write($sb);
            $this->out->close();
        }
    }

    /**
     * @return null
     */
    public function getExtension()
    {
        return null;
    }
}
<?php
/**
 * $Id: 8f7dc1ae4c1d6ac3a4d531968cb58034ad9bc754 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/phpunit/formatter/PHPUnitResultFormatter.php';

/**
 * Prints XML output of the test to a specified Writer
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 8f7dc1ae4c1d6ac3a4d531968cb58034ad9bc754 $
 * @package phing.tasks.ext.formatter
 * @since 2.1.0
 */
class XMLPHPUnitResultFormatter extends PHPUnitResultFormatter
{
    /**
     * @var PHPUnit_Util_Log_JUnit
     */
    private $logger = null;

    /**
     * @param PHPUnitTask $parentTask
     */
    public function __construct(PHPUnitTask $parentTask)
    {
        parent::__construct($parentTask);

        $logIncompleteSkipped = $parentTask->getHaltonincomplete() || $parentTask->getHaltonskipped();

        $this->logger = new PHPUnit_Util_Log_JUnit(null, $logIncompleteSkipped);
        $this->logger->setWriteDocument(false);
    }

    /**
     * @return string
     */
    public function getExtension()
    {
        return ".xml";
    }

    /**
     * @return string
     */
    public function getPreferredOutfile()
    {
        return "testsuites";
    }

    /**
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        parent::startTestSuite($suite);

        $this->logger->startTestSuite($suite);
    }

    /**
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        parent::endTestSuite($suite);

        $this->logger->endTestSuite($suite);
    }

    /**
     * @param PHPUnit_Framework_Test $test
     */
    public function startTest(PHPUnit_Framework_Test $test)
    {
        parent::startTest($test);

        $this->logger->startTest($test);
    }

    /**
     * @param PHPUnit_Framework_Test $test
     * @param float $time
     */
    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
        parent::endTest($test, $time);

        $this->logger->endTest($test, $time);
    }

    /**
     * @param PHPUnit_Framework_Test $test
     * @param Exception $e
     * @param float $time
     */
    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        parent::addError($test, $e, $time);

        $this->logger->addError($test, $e, $time);
    }

    /**
     * @param PHPUnit_Framework_Test $test
     * @param PHPUnit_Framework_AssertionFailedError $e
     * @param float $time
     */
    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
    {
        parent::addFailure($test, $e, $time);

        $this->logger->addFailure($test, $e, $time);
    }

    /**
     * @param PHPUnit_Framework_Test $test
     * @param Exception $e
     * @param float $time
     */
    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        parent::addIncompleteTest($test, $e, $time);

        $this->logger->addIncompleteTest($test, $e, $time);
    }

    public function endTestRun()
    {
        parent::endTestRun();

        if ($this->out) {
            $this->out->write($this->logger->getXML());
            $this->out->close();
        }
    }
}
<?php
/**
 * $Id: d9772e92cb3f8171dcfe721066c2f23f46f20c58 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/PhingFile.php';

/**
 * A wrapper for the implementations of PHPUnit2ResultFormatter.
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: d9772e92cb3f8171dcfe721066c2f23f46f20c58 $
 * @package phing.tasks.ext.phpunit
 * @since 2.1.0
 */
class FormatterElement
{
    protected $formatter = null;

    protected $type = "";

    protected $useFile = true;

    protected $toDir = ".";

    protected $outfile = "";

    protected $parent = null;

    /**
     * Sets parent task
     * @param Task $parent Calling Task
     */
    public function setParent($parent)
    {
        $this->parent = $parent;
    }

    /**
     * Loads a specific formatter type
     * @param string $type
     */
    public function setType($type)
    {
        $this->type = $type;
    }

    /**
     * Loads a specific formatter class
     * @param $className
     */
    public function setClassName($className)
    {
        $classNameNoDot = Phing::import($className);

        $this->formatter = new $classNameNoDot();
    }

    /**
     * Sets whether to store formatting results in a file
     * @param $useFile
     */
    public function setUseFile($useFile)
    {
        $this->useFile = $useFile;
    }

    /**
     * Returns whether to store formatting results in a file
     */
    public function getUseFile()
    {
        return $this->useFile;
    }

    /**
     * Sets output directory
     * @param string $toDir
     */
    public function setToDir($toDir)
    {
        if (!is_dir($toDir)) {
            $toDir = new PhingFile($toDir);
            $toDir->mkdirs();
        }

        $this->toDir = $toDir;
    }

    /**
     * Returns output directory
     * @return string
     */
    public function getToDir()
    {
        return $this->toDir;
    }

    /**
     * Sets output filename
     * @param string $outfile
     */
    public function setOutfile($outfile)
    {
        $this->outfile = $outfile;
    }

    /**
     * Returns output filename
     * @return string
     */
    public function getOutfile()
    {
        if ($this->outfile) {
            return $this->outfile;
        } else {
            return $this->formatter->getPreferredOutfile() . $this->getExtension();
        }
    }

    /**
     * Returns extension
     * @return string
     */
    public function getExtension()
    {
        return $this->formatter->getExtension();
    }

    /**
     * Returns formatter object
     * @throws BuildException
     * @return PHPUnitResultFormatter
     */
    public function getFormatter()
    {
        if ($this->formatter !== null) {
            return $this->formatter;
        }

        if ($this->type == "summary") {
            require_once 'phing/tasks/ext/phpunit/formatter/SummaryPHPUnitResultFormatter.php';
            $this->formatter = new SummaryPHPUnitResultFormatter($this->parent);
        } elseif ($this->type == "clover") {
            require_once 'phing/tasks/ext/phpunit/formatter/CloverPHPUnitResultFormatter.php';
            $this->formatter = new CloverPHPUnitResultFormatter($this->parent);
        } elseif ($this->type == "xml") {
            require_once 'phing/tasks/ext/phpunit/formatter/XMLPHPUnitResultFormatter.php';
            $this->formatter = new XMLPHPUnitResultFormatter($this->parent);
        } elseif ($this->type == "plain") {
            require_once 'phing/tasks/ext/phpunit/formatter/PlainPHPUnitResultFormatter.php';
            $this->formatter = new PlainPHPUnitResultFormatter($this->parent);
        } elseif ($this->type == "crap4j") {
            require_once 'phing/tasks/ext/phpunit/formatter/Crap4jPHPUnitResultFormatter.php';
            $this->formatter = new Crap4jPHPUnitResultFormatter($this->parent);
        } else {
            throw new BuildException("Formatter '" . $this->type . "' not implemented");
        }

        return $this->formatter;
    }
}
<?php
/**
 * $Id: 73ee928b954e62bad8f9e4b644a28e56c1e0df85 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/system/io/PhingFile.php';
require_once 'phing/system/io/FileWriter.php';
require_once 'phing/util/ExtendedFileStream.php';

/**
 * Transform a PHPUnit xml report using XSLT.
 * This transformation generates an html report in either framed or non-framed
 * style. The non-framed style is convenient to have a concise report via mail,
 * the framed report is much more convenient if you want to browse into
 * different packages or testcases since it is a Javadoc like report.
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 73ee928b954e62bad8f9e4b644a28e56c1e0df85 $
 * @package phing.tasks.ext.phpunit
 * @since 2.1.0
 */
class PHPUnitReportTask extends Task
{
    private $format = "noframes";
    private $styleDir = "";

    /**
     * @var PhingFile
     */
    private $toDir;

    /**
     * Whether to use the sorttable JavaScript library, defaults to false
     * See {@link http://www.kryogenix.org/code/browser/sorttable/)}
     *
     * @var boolean
     */
    private $useSortTable = false;

    /** the directory where the results XML can be found */
    private $inFile = "testsuites.xml";

    /**
     * Set the filename of the XML results file to use.
     * @param PhingFile $inFile
     * @return void
     */
    public function setInFile(PhingFile $inFile)
    {
        $this->inFile = $inFile;
    }

    /**
     * Set the format of the generated report. Must be noframes or frames.
     * @param $format
     * @return void
     */
    public function setFormat($format)
    {
        $this->format = $format;
    }

    /**
     * Set the directory where the stylesheets are located.
     * @param $styleDir
     * @return void
     */
    public function setStyleDir($styleDir)
    {
        $this->styleDir = $styleDir;
    }

    /**
     * Set the directory where the files resulting from the
     * transformation should be written to.
     * @param PhingFile $toDir
     * @return void
     */
    public function setToDir(PhingFile $toDir)
    {
        $this->toDir = $toDir;
    }

    /**
     * Sets whether to use the sorttable JavaScript library, defaults to false
     * See {@link http://www.kryogenix.org/code/browser/sorttable/)}
     *
     * @param boolean $useSortTable
     * @return void
     */
    public function setUseSortTable($useSortTable)
    {
        $this->useSortTable = (boolean) $useSortTable;
    }

    /**
     * Returns the path to the XSL stylesheet
     * @throws BuildException
     */
    protected function getStyleSheet()
    {
        $xslname = "phpunit-" . $this->format . ".xsl";

        if ($this->styleDir) {
            $file = new PhingFile($this->styleDir, $xslname);
        } else {
            $path = Phing::getResourcePath("phing/etc/$xslname");

            if ($path === null) {
                $path = Phing::getResourcePath("etc/$xslname");

                if ($path === null) {
                    throw new BuildException("Could not find $xslname in resource path");
                }
            }

            $file = new PhingFile($path);
        }

        if (!$file->exists()) {
            throw new BuildException("Could not find file " . $file->getPath());
        }

        return $file;
    }

    /**
     * Transforms the DOM document
     * @param DOMDocument $document
     * @throws BuildException
     * @throws IOException
     */
    protected function transform(DOMDocument $document)
    {
        if (!$this->toDir->exists()) {
            throw new BuildException("Directory '" . $this->toDir . "' does not exist");
        }

        $xslfile = $this->getStyleSheet();

        $xsl = new DOMDocument();
        $xsl->load($xslfile->getAbsolutePath());

        $proc = new XSLTProcessor();
        if (defined('XSL_SECPREF_WRITE_FILE')) {
            if (version_compare(PHP_VERSION, '5.4', "<")) {
                ini_set("xsl.security_prefs", XSL_SECPREF_WRITE_FILE | XSL_SECPREF_CREATE_DIRECTORY);
            } else {
                $proc->setSecurityPrefs(XSL_SECPREF_WRITE_FILE | XSL_SECPREF_CREATE_DIRECTORY);
            }
        }

        $proc->importStylesheet($xsl);
        $proc->setParameter('', 'output.sorttable', (string) $this->useSortTable);

        if ($this->format == "noframes") {
            $writer = new FileWriter(new PhingFile($this->toDir, "phpunit-noframes.html"));
            $writer->write($proc->transformToXml($document));
            $writer->close();
        } else {
            ExtendedFileStream::registerStream();

            $toDir = (string) $this->toDir;

            // urlencode() the path if we're on Windows
            if (FileSystem::getFileSystem()->getSeparator() == '\\') {
                $toDir = urlencode($toDir);
            }

            // no output for the framed report
            // it's all done by extension...
            $proc->setParameter('', 'output.dir', $toDir);
            $proc->transformToXml($document);

            ExtendedFileStream::unregisterStream();
        }
    }

    /**
     * Fixes DOM document tree:
     *   - adds package="default" to 'testsuite' elements without
     *     package attribute
     *   - removes outer 'testsuite' container(s)
     * @param DOMDocument $document
     */
    protected function fixDocument(DOMDocument $document)
    {
        $rootElement = $document->firstChild;

        $xp = new DOMXPath($document);

        $nodes = $xp->query("/testsuites/testsuite");

        foreach ($nodes as $node) {
            $children = $xp->query("./testsuite", $node);

            if ($children->length) {
                /** @var $child DOMElement */
                foreach ($children as $child) {
                    $rootElement->appendChild($child);

                    if ($child->hasAttribute('package')) {
                        continue;
                    }

                    if ($child->hasAttribute('namespace')) {
                        $child->setAttribute('package', $child->getAttribute('namespace'));
                        continue;
                    }

                    $package = 'default';
                    $refClass = new ReflectionClass($child->getAttribute('name'));

                    if (preg_match('/@package\s+(.*)\r?\n/m', $refClass->getDocComment(), $matches)) {
                        $package = end($matches);
                    } elseif (method_exists($refClass, 'getNamespaceName')) {
                        $namespace = $refClass->getNamespaceName();

                        if ($namespace !== '') {
                            $package = $namespace;
                        }
                    }

                    $child->setAttribute('package', trim($package));
                }

                $rootElement->removeChild($node);
            }
        }
    }

    /**
     * Initialize the task
     */
    public function init()
    {
        if (!class_exists('XSLTProcessor')) {
            throw new BuildException("PHPUnitReportTask requires the XSL extension");
        }
    }

    /**
     * The main entry point
     *
     * @throws BuildException
     */
    public function main()
    {
        $testSuitesDoc = new DOMDocument();
        $testSuitesDoc->load((string) $this->inFile);

        $this->fixDocument($testSuitesDoc);

        $this->transform($testSuitesDoc);
    }
}
<?php
/**
 * $Id: 8db5f2b968a9a111de7bbd1f1ddbc9f5a3c9d55a $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/system/io/PhingFile.php';
require_once 'phing/system/io/Writer.php';
require_once 'phing/util/LogWriter.php';
require_once 'phing/tasks/ext/phpunit/BatchTest.php';
require_once 'phing/tasks/ext/phpunit/FormatterElement.php';

/**
 * Runs PHPUnit tests.
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 8db5f2b968a9a111de7bbd1f1ddbc9f5a3c9d55a $
 * @package phing.tasks.ext.phpunit
 * @see BatchTest
 * @since 2.1.0
 */
class PHPUnitTask extends Task
{
    private $batchtests = array();
    private $formatters = array();
    private $bootstrap = "";
    private $haltonerror = false;
    private $haltonfailure = false;
    private $haltonincomplete = false;
    private $haltonskipped = false;
    private $errorproperty;
    private $failureproperty;
    private $incompleteproperty;
    private $skippedproperty;
    private $printsummary = false;
    private $testfailed = false;
    private $testfailuremessage = "";
    private $codecoverage = null;
    private $groups = array();
    private $excludeGroups = array();
    private $processIsolation = false;
    private $usecustomerrorhandler = true;
    private $listeners = array();

    /**
     * @var string
     */
    private $pharLocation = "";

    /**
     * @var PhingFile
     */
    private $configuration = null;

    /**
     * Initialize Task.
     * This method includes any necessary PHPUnit libraries and triggers
     * appropriate error if they cannot be found.  This is not done in header
     * because we may want this class to be loaded w/o triggering an error.
     */
    public function init()
    {
    }

    private function loadPHPUnit()
    {
        /**
         * Determine PHPUnit version number, try
         * PEAR old-style, then composer, then PHAR
         */
        @include_once 'PHPUnit/Runner/Version.php';
        if (!class_exists('PHPUnit_Runner_Version')) {
            @include_once 'phpunit/Runner/Version.php';
        }
        if (!empty($this->pharLocation)) {
            $GLOBALS['_SERVER']['SCRIPT_NAME'] = '-';
            ob_start();
            @include $this->pharLocation;
            ob_end_clean();
        }
        @include_once 'PHPUnit/Autoload.php';

        if (!class_exists('PHPUnit_Runner_Version')) {
            throw new BuildException("PHPUnitTask requires PHPUnit to be installed", $this->getLocation());
        }

        $version = PHPUnit_Runner_Version::id();

        if (version_compare($version, '3.6.0') < 0) {
            throw new BuildException("PHPUnitTask requires PHPUnit version >= 3.6.0", $this->getLocation());
        }

        /**
         * Other dependencies that should only be loaded when class is actually used.
         */
        require_once 'phing/tasks/ext/phpunit/PHPUnitTestRunner.php';

        /**
         * point PHPUnit_MAIN_METHOD define to non-existing method
         */
        if (!defined('PHPUnit_MAIN_METHOD')) {
            define('PHPUnit_MAIN_METHOD', 'PHPUnitTask::undefined');
        }
    }

    /**
     * Sets the name of a bootstrap file that is run before
     * executing the tests
     *
     * @param string $bootstrap the name of the bootstrap file
     */
    public function setBootstrap($bootstrap)
    {
        $this->bootstrap = $bootstrap;
    }

    /**
     * @param $value
     */
    public function setErrorproperty($value)
    {
        $this->errorproperty = $value;
    }

    /**
     * @param $value
     */
    public function setFailureproperty($value)
    {
        $this->failureproperty = $value;
    }

    /**
     * @param $value
     */
    public function setIncompleteproperty($value)
    {
        $this->incompleteproperty = $value;
    }

    /**
     * @param $value
     */
    public function setSkippedproperty($value)
    {
        $this->skippedproperty = $value;
    }

    /**
     * @param $value
     */
    public function setHaltonerror($value)
    {
        $this->haltonerror = $value;
    }

    /**
     * @param $value
     */
    public function setHaltonfailure($value)
    {
        $this->haltonfailure = $value;
    }

    /**
     * @return bool
     */
    public function getHaltonfailure()
    {
        return $this->haltonfailure;
    }

    /**
     * @param $value
     */
    public function setHaltonincomplete($value)
    {
        $this->haltonincomplete = $value;
    }

    /**
     * @return bool
     */
    public function getHaltonincomplete()
    {
        return $this->haltonincomplete;
    }

    /**
     * @param $value
     */
    public function setHaltonskipped($value)
    {
        $this->haltonskipped = $value;
    }

    /**
     * @return bool
     */
    public function getHaltonskipped()
    {
        return $this->haltonskipped;
    }

    /**
     * @param $printsummary
     */
    public function setPrintsummary($printsummary)
    {
        $this->printsummary = $printsummary;
    }

    /**
     * @param $codecoverage
     */
    public function setCodecoverage($codecoverage)
    {
        $this->codecoverage = $codecoverage;
    }

    /**
     * @param $processIsolation
     */
    public function setProcessIsolation($processIsolation)
    {
        $this->processIsolation = $processIsolation;
    }

    /**
     * @param $usecustomerrorhandler
     */
    public function setUseCustomErrorHandler($usecustomerrorhandler)
    {
        $this->usecustomerrorhandler = $usecustomerrorhandler;
    }

    /**
     * @param $groups
     */
    public function setGroups($groups)
    {
        $token = ' ,;';
        $this->groups = array();
        $tok = strtok($groups, $token);
        while ($tok !== false) {
            $this->groups[] = $tok;
            $tok = strtok($token);
        }
    }

    /**
     * @param $excludeGroups
     */
    public function setExcludeGroups($excludeGroups)
    {
        $token = ' ,;';
        $this->excludeGroups = array();
        $tok = strtok($excludeGroups, $token);
        while ($tok !== false) {
            $this->excludeGroups[] = $tok;
            $tok = strtok($token);
        }
    }

    /**
     * Add a new formatter to all tests of this task.
     *
     * @param FormatterElement formatter element
     */
    public function addFormatter(FormatterElement $fe)
    {
        $fe->setParent($this);
        $this->formatters[] = $fe;
    }

    /**
     * Add a new listener to all tests of this taks
     *
     * @param $listener
     */
    private function addListener($listener)
    {
        $this->listeners[] = $listener;
    }

    /**
     * @param PhingFile $configuration
     */
    public function setConfiguration(PhingFile $configuration)
    {
        $this->configuration = $configuration;
    }

    /**
     * @param string $pharLocation
     */
    public function setPharLocation($pharLocation)
    {
        $this->pharLocation = $pharLocation;
    }

    /**
     * Load and processes the PHPUnit configuration
     * @param $configuration
     * @throws BuildException
     * @return array
     */
    protected function handlePHPUnitConfiguration($configuration)
    {
        if (!$configuration->exists()) {
            throw new BuildException("Unable to find PHPUnit configuration file '" . (string) $configuration . "'");
        }

        $config = PHPUnit_Util_Configuration::getInstance($configuration->getAbsolutePath());

        if (empty($config)) {
            return;
        }

        $phpunit = $config->getPHPUnitConfiguration();

        if (empty($phpunit)) {
            return;
        }

        $config->handlePHPConfiguration();

        if (isset($phpunit['bootstrap'])) {
            $this->setBootstrap($phpunit['bootstrap']);
        }

        if (isset($phpunit['stopOnFailure'])) {
            $this->setHaltonfailure($phpunit['stopOnFailure']);
        }

        if (isset($phpunit['stopOnError'])) {
            $this->setHaltonerror($phpunit['stopOnError']);
        }

        if (isset($phpunit['stopOnSkipped'])) {
            $this->setHaltonskipped($phpunit['stopOnSkipped']);
        }

        if (isset($phpunit['stopOnIncomplete'])) {
            $this->setHaltonincomplete($phpunit['stopOnIncomplete']);
        }

        if (isset($phpunit['processIsolation'])) {
            $this->setProcessIsolation($phpunit['processIsolation']);
        }

        foreach ($config->getListenerConfiguration() as $listener) {
            if (!class_exists($listener['class'], false) &&
                $listener['file'] !== '') {
                require_once $listener['file'];
            }

            if (class_exists($listener['class'])) {
                if (count($listener['arguments']) == 0) {
                    $listener = new $listener['class'];
                } else {
                    $listenerClass = new ReflectionClass(
                                       $listener['class']
                                     );
                    $listener      = $listenerClass->newInstanceArgs(
                                       $listener['arguments']
                                     );
                }

                if ($listener instanceof PHPUnit_Framework_TestListener) {
                    $this->addListener($listener);
                }
            }
        }

        if (method_exists($config, 'getSeleniumBrowserConfiguration')) {
            $browsers = $config->getSeleniumBrowserConfiguration();

            if (!empty($browsers) &&
                class_exists('PHPUnit_Extensions_SeleniumTestCase')
            ) {
                PHPUnit_Extensions_SeleniumTestCase::$browsers = $browsers;
            }
        }

        return $phpunit;
    }

    /**
     * The main entry point
     *
     * @throws BuildException
     */
    public function main()
    {
        if ($this->codecoverage && !extension_loaded('xdebug')) {
            throw new Exception("PHPUnitTask depends on Xdebug being installed to gather code coverage information.");
        }

        $this->loadPHPUnit();

        $suite = new PHPUnit_Framework_TestSuite('AllTests');

        $autoloadSave = spl_autoload_functions();

        if ($this->bootstrap) {
            require $this->bootstrap;
        }

        if ($this->configuration) {
            $arguments = $this->handlePHPUnitConfiguration($this->configuration);

            if ($arguments['backupGlobals'] === false) {
                $suite->setBackupGlobals(false);
            }

            if ($arguments['backupStaticAttributes'] === true) {
                $suite->setBackupStaticAttributes(true);
            }
        }

        if ($this->printsummary) {
            $fe = new FormatterElement();
            $fe->setParent($this);
            $fe->setType("summary");
            $fe->setUseFile(false);
            $this->formatters[] = $fe;
        }

        foreach ($this->batchtests as $batchTest) {
            $this->appendBatchTestToTestSuite($batchTest, $suite);
        }

        $this->execute($suite);

        if ($this->testfailed) {
            throw new BuildException($this->testfailuremessage);
        }

        $autoloadNew = spl_autoload_functions();
        if(is_array($autoloadNew)) {
            foreach ($autoloadNew as $autoload) {
                spl_autoload_unregister($autoload);
            }
        }

        if(is_array($autoloadSave)) {
            foreach ($autoloadSave as $autoload) {
                spl_autoload_register($autoload);
            }
        }
    }

    /**
     * @param $suite
     */
    protected function execute($suite)
    {
        $runner = new PHPUnitTestRunner($this->project, $this->groups, $this->excludeGroups, $this->processIsolation);

        if ($this->codecoverage) {
            /**
             * Add some defaults to the PHPUnit filter
             */
            $pwd = dirname(__FILE__);
            $path = realpath($pwd . '/../../../');

            if (class_exists('PHP_CodeCoverage_Filter')) {
                $filter = new PHP_CodeCoverage_Filter();
            } elseif (class_exists('\SebastianBergmann\CodeCoverage\Filter')) {
                $filterClass = '\SebastianBergmann\CodeCoverage\Filter';
                $filter = new $filterClass;
            }
            if (method_exists($filter, 'addDirectoryToBlacklist')) {
                $filter->addDirectoryToBlacklist($path);
            }
            if (class_exists('PHP_CodeCoverage')) {
                $codeCokverage = new PHP_CodeCoverage(null, $filter);
            } elseif (class_exists('\SebastianBergmann\CodeCoverage\CodeCoverage')) {
                $codeCokverageClass = '\SebastianBergmann\CodeCoverage\CodeCoverage';
                $codeCokverage = new $codeCokverageClass(null, $filter);
            }
            $runner->setCodecoverage($codeCokverage);
        }

        $runner->setUseCustomErrorHandler($this->usecustomerrorhandler);

        foreach ($this->listeners as $listener) {
            $runner->addListener($listener);
        }

        foreach ($this->formatters as $fe) {
            $formatter = $fe->getFormatter();

            if ($fe->getUseFile()) {
                $destFile = new PhingFile($fe->getToDir(), $fe->getOutfile());

                $writer = new FileWriter($destFile->getAbsolutePath());

                $formatter->setOutput($writer);
            } else {
                $formatter->setOutput($this->getDefaultOutput());
            }

            $runner->addFormatter($formatter);

            $formatter->startTestRun();
        }

        $runner->run($suite);

        foreach ($this->formatters as $fe) {
            $formatter = $fe->getFormatter();
            $formatter->endTestRun();
        }

        if ($runner->hasErrors()) {
            if ($this->errorproperty) {
                $this->project->setNewProperty($this->errorproperty, true);
            }
            if ($this->haltonerror) {
                $this->testfailed = true;
                $this->testfailuremessage = $runner->getLastErrorMessage();
            }
        }

        if ($runner->hasFailures()) {
            if ($this->failureproperty) {
                $this->project->setNewProperty($this->failureproperty, true);
            }

            if ($this->haltonfailure) {
                $this->testfailed = true;
                $this->testfailuremessage = $runner->getLastFailureMessage();
            }
        }

        if ($runner->hasIncomplete()) {
            if ($this->incompleteproperty) {
                $this->project->setNewProperty($this->incompleteproperty, true);
            }

            if ($this->haltonincomplete) {
                $this->testfailed = true;
                $this->testfailuremessage = $runner->getLastIncompleteMessage();
            }
        }

        if ($runner->hasSkipped()) {
            if ($this->skippedproperty) {
                $this->project->setNewProperty($this->skippedproperty, true);
            }

            if ($this->haltonskipped) {
                $this->testfailed = true;
                $this->testfailuremessage = $runner->getLastSkippedMessage();
            }
        }
    }

    /**
     * Add the tests in this batchtest to a test suite
     *
     * @param BatchTest                   $batchTest
     * @param PHPUnit_Framework_TestSuite $suite
     */
    protected function appendBatchTestToTestSuite(BatchTest $batchTest, PHPUnit_Framework_TestSuite $suite)
    {
        foreach ($batchTest->elements() as $element) {
            $testClass = new $element();
            if (!($testClass instanceof PHPUnit_Framework_TestSuite)) {
                $testClass = new ReflectionClass($element);
            }
            $suite->addTestSuite($testClass);
        }
    }

    /**
     * @return LogWriter
     */
    protected function getDefaultOutput()
    {
        return new LogWriter($this);
    }

    /**
     * Adds a set of tests based on pattern matching.
     *
     * @return BatchTest a new instance of a batch test.
     */
    public function createBatchTest()
    {
        $batchtest = new BatchTest($this->getProject());

        $this->batchtests[] = $batchtest;

        return $batchtest;
    }
}
<?php
/**
 * $Id: 77ea1859b96ca9889af0e465b1a27a09600557da $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/coverage/CoverageMerger.php';
require_once 'phing/system/util/Timer.php';

/**
 * Simple Testrunner for PHPUnit that runs all tests of a testsuite.
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 77ea1859b96ca9889af0e465b1a27a09600557da $
 * @package phing.tasks.ext.phpunit
 * @since 2.1.0
 */
class PHPUnitTestRunner extends PHPUnit_Runner_BaseTestRunner implements PHPUnit_Framework_TestListener
{
    private $hasErrors = false;
    private $hasFailures = false;
    private $hasIncomplete = false;
    private $hasSkipped = false;
    private $lastErrorMessage = '';
    private $lastFailureMessage = '';
    private $lastIncompleteMessage = '';
    private $lastSkippedMessage = '';
    private $formatters = array();
    private $listeners = array();

    private $codecoverage = null;

    private $project = null;

    private $groups = array();
    private $excludeGroups = array();

    private $processIsolation = false;

    private $useCustomErrorHandler = true;

    /**
     * @param Project $project
     * @param array $groups
     * @param array $excludeGroups
     * @param bool $processIsolation
     */
    public function __construct(
        Project $project,
        $groups = array(),
        $excludeGroups = array(),
        $processIsolation = false
    ) {
        $this->project = $project;
        $this->groups = $groups;
        $this->excludeGroups = $excludeGroups;
        $this->processIsolation = $processIsolation;
    }

    /**
     * @param $codecoverage
     */
    public function setCodecoverage($codecoverage)
    {
        $this->codecoverage = $codecoverage;
    }

    /**
     * @param $useCustomErrorHandler
     */
    public function setUseCustomErrorHandler($useCustomErrorHandler)
    {
        $this->useCustomErrorHandler = $useCustomErrorHandler;
    }

    /**
     * @param $formatter
     */
    public function addFormatter($formatter)
    {
        $this->addListener($formatter);
        $this->formatters[] = $formatter;
    }

    /**
     * @param $level
     * @param $message
     * @param $file
     * @param $line
     */
    public function handleError($level, $message, $file, $line)
    {
        return PHPUnit_Util_ErrorHandler::handleError($level, $message, $file, $line);
    }

    public function addListener($listener)
    {
        $this->listeners[] = $listener;
    }

    /**
     * Run a test
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function run(PHPUnit_Framework_TestSuite $suite)
    {
        $res = new PHPUnit_Framework_TestResult();

        if ($this->codecoverage) {
            $whitelist = CoverageMerger::getWhiteList($this->project);

            $this->codecoverage->filter()->addFilesToWhiteList($whitelist);

            $res->setCodeCoverage($this->codecoverage);
        }

        $res->addListener($this);

        foreach ($this->formatters as $formatter) {
            $res->addListener($formatter);
        }

        /* Set PHPUnit error handler */
        if ($this->useCustomErrorHandler) {
            $oldErrorHandler = set_error_handler(array($this, 'handleError'), E_ALL | E_STRICT);
        }

        $version = PHPUnit_Runner_Version::id();
        if (version_compare($version, '4.0.0') >= 0) {
            $this->injectFilters($suite);
            $suite->run($res);
        } else {
            $suite->run($res, false, $this->groups, $this->excludeGroups, $this->processIsolation);
        }

        foreach ($this->formatters as $formatter) {
            $formatter->processResult($res);
        }

        /* Restore Phing error handler */
        if ($this->useCustomErrorHandler) {
            restore_error_handler();
        }

        if ($this->codecoverage) {
            CoverageMerger::merge($this->project, $this->codecoverage->getData());
        }

        $this->checkResult($res);
    }

    private function checkResult($res)
    {
        if ($res->skippedCount() > 0) {
            $this->hasSkipped = true;
        }

        if ($res->notImplementedCount() > 0) {
            $this->hasIncomplete = true;
        }

        if ($res->failureCount() > 0) {
            $this->hasFailures = true;
        }

        if ($res->errorCount() > 0) {
            $this->hasErrors = true;
        }
    }

    /**
     * @return boolean
     */
    public function hasErrors()
    {
        return $this->hasErrors;
    }

    /**
     * @return boolean
     */
    public function hasFailures()
    {
        return $this->hasFailures;
    }

    /**
     * @return boolean
     */
    public function hasIncomplete()
    {
        return $this->hasIncomplete;
    }

    /**
     * @return boolean
     */
    public function hasSkipped()
    {
        return $this->hasSkipped;
    }

    /**
     * @return string
     */
    public function getLastErrorMessage()
    {
        return $this->lastErrorMessage;
    }

    /**
     * @return string
     */
    public function getLastFailureMessage()
    {
        return $this->lastFailureMessage;
    }

    /**
     * @return string
     */
    public function getLastIncompleteMessage()
    {
        return $this->lastIncompleteMessage;
    }

    /**
     * @return string
     */
    public function getLastSkippedMessage()
    {
        return $this->lastSkippedMessage;
    }

    /**
     * @param string $message
     * @param PHPUnit_Framework_Test $test
     * @param Exception $e
     * @return string
     */
    protected function composeMessage($message, PHPUnit_Framework_Test $test, Exception $e)
    {
        $message = "Test $message (" . $test->getName() . " in class " . get_class($test) . "): " . $e->getMessage();

        if ($e instanceof PHPUnit_Framework_ExpectationFailedException && $e->getComparisonFailure()) {
            $message .= "\n" . $e->getComparisonFailure()->getDiff();
        }

        return $message;
    }

    /**
     * An error occurred.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->lastErrorMessage = $this->composeMessage("ERROR", $test, $e);
    }

    /**
     * A failure occurred.
     *
     * @param PHPUnit_Framework_Test                 $test
     * @param PHPUnit_Framework_AssertionFailedError $e
     * @param float                                  $time
     */
    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
    {
        $this->lastFailureMessage = $this->composeMessage("FAILURE", $test, $e);
    }

    /**
     * Incomplete test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->lastIncompleteMessage = $this->composeMessage("INCOMPLETE", $test, $e);
    }

    /**
     * Skipped test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     * @since  Method available since Release 3.0.0
     */
    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->lastSkippedMessage = $this->composeMessage("SKIPPED", $test, $e);
    }

    /**
     * Risky test
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
    }

    /**
     * A test started.
     *
     * @param string $testName
     */
    public function testStarted($testName)
    {
    }

    /**
     * A test ended.
     *
     * @param string $testName
     */
    public function testEnded($testName)
    {
    }

    /**
     * A test failed.
     *
     * @param integer                                $status
     * @param PHPUnit_Framework_Test                 $test
     * @param PHPUnit_Framework_AssertionFailedError $e
     */
    public function testFailed($status, PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e)
    {
    }

    /**
     * Override to define how to handle a failed loading of
     * a test suite.
     *
     * @param string $message
     * @throws BuildException
     */
    protected function runFailed($message)
    {
        throw new BuildException($message);
    }

    /**
     * A test suite started.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     * @since  Method available since Release 2.2.0
     */
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
    }

    /**
     * A test suite ended.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     * @since  Method available since Release 2.2.0
     */
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
    }

    /**
     * A test started.
     *
     * @param PHPUnit_Framework_Test $test
     */
    public function startTest(PHPUnit_Framework_Test $test)
    {
    }

    /**
     * A test ended.
     *
     * @param PHPUnit_Framework_Test $test
     * @param float                  $time
     */
    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
        if ($test instanceof PHPUnit_Framework_TestCase) {
            if (!$test->hasPerformedExpectationsOnOutput()) {
                echo $test->getActualOutput();
            }
        }
    }

    /**
     * @param PHPUnit_Framework_TestSuite $suite
     */
    private function injectFilters(PHPUnit_Framework_TestSuite $suite)
    {
        $filterFactory = new PHPUnit_Runner_Filter_Factory();

        if (!empty($this->excludeGroups)) {
            $filterFactory->addFilter(
                new ReflectionClass('PHPUnit_Runner_Filter_Group_Exclude'),
                $this->excludeGroups
            );
        }

        if (!empty($this->groups)) {
            $filterFactory->addFilter(
                new ReflectionClass('PHPUnit_Runner_Filter_Group_Include'),
                $this->groups
            );
        }

        $suite->injectFilter($filterFactory);
    }
}
<?php
/**
 * $Id: 2bed201fd0c78712be2bc9c1eab23f75e646cf4c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Various utility functions
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 2bed201fd0c78712be2bc9c1eab23f75e646cf4c $
 * @package phing.tasks.ext.phpunit
 * @since 2.1.0
 */
class PHPUnitUtil
{
    protected static $definedClasses = array();

    /**
     * Returns the package of a class as defined in the docblock of the class using @package
     *
     * @param string the name of the class
     * @return string the name of the package
     */
    public static function getPackageName($classname)
    {
        $reflect = new ReflectionClass($classname);

        if (method_exists($reflect, 'getNamespaceName')) {
            $namespace = $reflect->getNamespaceName();

            if ($namespace != '') {
                return $namespace;
            }
        }

        if (preg_match('/@package[\s]+([\.\w]+)/', $reflect->getDocComment(), $matches)) {
            return $matches[1];
        }

        return "default";
    }

    /**
     * Returns the subpackage of a class as defined in the docblock of the class
     * using @subpackage
     *
     * @param string $classname the name of the class
     *
     * @author Benjamin Schultz <bschultz@proqrent.de>
     * @return string|null the name of the subpackage
     */
    public static function getSubpackageName($classname)
    {
        $reflect = new ReflectionClass($classname);

        if (preg_match('/@subpackage[\s]+([\.\w]+)/', $reflect->getDocComment(), $matches)) {
            return $matches[1];
        } else {
            return null;
        }
    }

    /**
     * Derives the classname from a filename.
     * Assumes that there is only one class defined in that particular file, and that
     * the naming follows the dot-path (Java) notation scheme.
     *
     * @param string the filename
     * @return string the name fo the class
     */
    public static function getClassFromFileName($filename)
    {
        $filename = basename($filename);

        $rpos = strrpos($filename, '.');

        if ($rpos != -1) {
            $filename = substr($filename, 0, $rpos);
        }

        return $filename;
    }

    /**
     * @param $filename
     * @param null $classpath
     * @throws Exception
     * @internal param the $string filename
     * @internal param optional $Path classpath
     * @return array list of classes defined in the file
     */
    public static function getDefinedClasses($filename, $classpath = null)
    {
        $filename = realpath($filename);

        if (!file_exists($filename)) {
            throw new Exception("File '" . $filename . "' does not exist");
        }

        if (isset(self::$definedClasses[$filename])) {
            return self::$definedClasses[$filename];
        }

        Phing::__import($filename, $classpath);

        $declaredClasses = get_declared_classes();

        foreach ($declaredClasses as $classname) {
            $reflect = new ReflectionClass($classname);

            self::$definedClasses[$reflect->getFilename()][] = $classname;

            if (is_array(self::$definedClasses[$reflect->getFilename()])) {
                self::$definedClasses[$reflect->getFilename()] = array_unique(
                    self::$definedClasses[$reflect->getFilename()]
                );
            }
        }

        if (isset(self::$definedClasses[$filename])) {
            return self::$definedClasses[$filename];
        } else {
            return array();
        }
    }
}
<?php
/**
 * $Id: 8a0e0d7afe9ac9416d6ec9ea82563f6f738cb709 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

abstract class AbstractPropertySetterTask extends Task
{
    private $property;
    private $override = false;

    public function setOverride($override)
    {
        $this->override = $override;
    }

    public function setProperty($property)
    {
        $this->property = $property;
    }

    protected function validate()
    {
        if ($this->property == null) {
            throw new BuildException("You must specify a property to set.");
        }
    }

    protected function setPropertyValue($value) {
        if ($value !== null) {
            if ($this->override) {
                if ($this->getProject()->getUserProperty($this->property) == null) {
                    $this->getProject()->setProperty($this->property, $value);
                } else {
                    $this->getProject()->setUserProperty($this->property, $value);
                }
            } else {
                $p = $this->project->createTask("property");
                $p->setName($this->property);
                $p->setValue($value);
                $p->main();
            }
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/BuildException.php';
include_once 'phing/Task.php';
include_once 'phing/system/io/PhingFile.php';
include_once 'phing/types/FileSet.php';
include_once 'phing/util/FileUtils.php';

/**
 * Coverts a path to a fileset.
 * This is useful if you have a path but need to use a fileset as input in a phing task.
 *
 * Example
 * =======
 *
 * ```
 *   <path id="modified.sources.path" dir="C:\Path\to\phing\classes\phing\" />
 *   <pathtofileset name="modified.sources.fileset"
 *                  pathrefid="modified.sources.path"
 *                  dir="." />
 *
 *   <copy todir="C:\Path\to\phing\docs\api">
 *     <mapper type="glob" from="*.php" to="*.php.bak" />
 *     <fileset refid="modified.sources.fileset" />
 *   </copy>
 * ```
 *
 * @author Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.tasks.ext.property
 */
class PathToFileSet extends Task
{
    /** @var PhingFile $dir */
    private $dir;

    /** @var string $name */
    private $name;

    /** @var string $pathRefId */
    private $pathRefId;

    /** @var bool $ignoreNonRelative */
    private $ignoreNonRelative = false;

    /**
     * @param PhingFile $dir
     */
    public function setDir(PhingFile $dir)
    {
        $this->dir = $dir;
    }

    /**
     * @param $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * @param $pathRefId
     */
    public function setPathRefId($pathRefId)
    {
        $this->pathRefId = $pathRefId;
    }

    /**
     * @param $ignoreNonRelative
     */
    public function setIgnoreNonRelative($ignoreNonRelative)
    {
        $this->ignoreNonRelative = $ignoreNonRelative;
    }

    /**
     * {@inheritdoc}
     *
     * @throws BuildException
     * @throws IOException
     */
    public function main()
    {
        if ($this->dir == null) {
            throw new BuildException("missing dir");
        }
        if ($this->name == null) {
            throw new BuildException("missing name");
        }
        if ($this->pathRefId == null) {
            throw new BuildException("missing pathrefid");
        }
        if (!$this->dir->isDirectory()) {
            throw new BuildException(
                $this->dir->toString() . " is not a directory");
        }
        $path = $this->getProject()->getReference($this->pathRefId);
        if ($path == null) {
            throw new BuildException("Unknown reference " . $this->pathRefId);
        }
        if (!($path instanceof Path)) {
            throw new BuildException($this->pathRefId . " is not a path");
        }
        $sources = $path->listPaths();
        $fileSet = new FileSet();
        $fileSet->setProject($this->getProject());
        $fileSet->setDir($this->dir);
        $fileUtils = new FileUtils();
        $dirNormal = $fileUtils->normalize($this->dir->getAbsolutePath());
        $dirNormal = rtrim($dirNormal, PhingFile::$separator) . PhingFile::$separator;

        $atLeastOne = false;
        for ($i = 0; $i < count($sources); ++$i) {
            $sourceFile = new PhingFile($sources[$i]);
            if (!$sourceFile->exists()) {
                continue;
            }
            $includePattern = $this->getIncludePattern($dirNormal, $sourceFile);
            if ($includePattern === false && !$this->ignoreNonRelative) {
                throw new BuildException(
                    $sources[$i] . " is not relative to " . $this->dir->getAbsolutePath());
            }
            if ($includePattern === false) {
                continue;
            }
            $fileSet->createInclude()->setName($includePattern);
            $atLeastOne = true;
        }
        if (!$atLeastOne) {
            $fileSet->createInclude()->setName("a:b:c:d//THis si &&& not a file !!! ");
        }
        $this->getProject()->addReference($this->name, $fileSet);
    }

    /**
     * @param string $dirNormal
     * @param PhingFile $file
     * @return string|false
     * @throws IOException
     */
    private function getIncludePattern($dirNormal, PhingFile $file)
    {
        $fileUtils = new FileUtils();
        $fileNormal = $fileUtils->normalize($file->getAbsolutePath());

        return rtrim(str_replace('\\', '/', substr($fileNormal, strlen($dirNormal))), '/') . '/';
    }
} 
<?php
/**
 * $Id: 5038479697b9cd62aa39006c53598ef0c41caac4 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/tasks/ext/property/AbstractPropertySetterTask.php';
include_once 'phing/util/regexp/Regexp.php';

/**
 * Regular Expression Task for properties.
 *
 * <pre>
 *   <propertyregex property="pack.name"
 *                  subject="package.ABC.name"
 *                  pattern="package\.([^.]*)\.name"
 *                  match="$1"
 *                  casesensitive="false"
 *                  defaultvalue="test1"/>
 *
 *   <echo message="${pack.name}"/>
 *
 *   <propertyregex property="pack.name"
 *                  override="true"
 *                  subject="package.ABC.name"
 *                  pattern="(package)\.[^.]*\.(name)"
 *                  replace="$1.DEF.$2"
 *                  casesensitive="false"
 *                  defaultvalue="test2"/>
 *
 *   <echo message="${pack.name}"/>
 *
 * </pre>
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing.tasks.regex
 */
class RegexTask extends AbstractPropertySetterTask
{
    /** @var string $subject */
    private $subject;

    /** @var string $pattern */
    private $pattern;

    /** @var string $match */
    private $match;

    /** @var string $replace */
    private $replace;

    /** @var string $defaultValue */
    private $defaultValue;

    /** @var bool $caseSensitive */
    private $caseSensitive = true;

    /** @var array $modifiers */
    private $modifiers = '';

    /** @var Regexp $reg */
    private $reg;

    /** @var int $limit */
    private $limit = -1;
    
    public function init()
    {
        $this->reg = new Regexp();
    }

    /**
     * @param int $limit
     */
    public function setLimit($limit)
    {
        $this->limit = $limit;
    }
    
    /**
     * @param string $subject
     */
    public function setSubject($subject)
    {
        $this->subject = $subject;
    }

    /**
     * @param string $defaultValue
     */
    public function setDefaultValue($defaultValue)
    {
        $this->log('Set default value to ' . $defaultValue, Project::MSG_DEBUG);

        $this->defaultValue = $defaultValue;
    }

    /**
     * @param  string $pattern
     * @throws BuildException
     */
    public function setPattern($pattern)
    {
        if ($this->pattern !== null) {
            throw new BuildException(
                'Cannot specify more than one regular expression'
            );
        }

        $this->log('Set pattern to ' . $pattern, Project::MSG_DEBUG);

        $this->pattern = $pattern;
    }

    /**
     * @param $replace
     * @throws BuildException
     */
    public function setReplace($replace)
    {
        if ($this->replace !== null) {
            throw new BuildException(
                'Cannot specify more than one replace expression'
            );
        }
        if ($this->match !== null) {
            throw new BuildException(
                'You cannot specify both a select and replace expression'
            );
        }

        $this->log('Set replace to ' . $replace, Project::MSG_DEBUG);

        $this->replace = $replace;
    }

    /**
     * @param $match
     * @throws BuildException
     */
    public function setMatch($match)
    {
        if ($this->match !== null) {
            throw new BuildException(
                'Cannot specify more than one match expression'
            );
        }

        $this->log('Set match to ' . $match, Project::MSG_DEBUG);

        $this->match = $match;
    }

    /**
     * @param $caseSensitive
     */
    public function setCaseSensitive($caseSensitive)
    {

        $this->log("Set case-sensitive to $caseSensitive", Project::MSG_DEBUG);

        $this->caseSensitive = $caseSensitive;
    }

    /**
     * @return mixed|string
     * @throws BuildException
     */
    protected function doReplace()
    {
        if ($this->replace === null) {
            throw new BuildException('No replace expression specified.');
        }
        $this->reg->setPattern($this->pattern);
        $this->reg->setReplace($this->replace);
        $this->reg->setModifiers($this->modifiers);
        $this->reg->setIgnoreCase(!$this->caseSensitive);
        $this->reg->setLimit($this->limit);

        try {
            $output = $this->reg->replace($this->subject);
        } catch (Exception $e) {
            $output = $this->defaultValue;
        }

        return $output;
    }

    /**
     * @return string
     *
     * @throws BuildException
     */
    protected function doSelect()
    {
        $this->reg->setPattern($this->pattern);
        $this->reg->setModifiers($this->modifiers);
        $this->reg->setIgnoreCase(!$this->caseSensitive);

        $output = $this->defaultValue;

        try {
            if ($this->reg->matches($this->subject)) {
                $output = $this->reg->getGroup((int) ltrim($this->match, '$'));
            }
        } catch (Exception $e) {
            throw new BuildException($e);
        }

        return $output;
    }

    /**
     * @throws BuildException
     */
    protected function validate()
    {
        if ($this->pattern === null) {
            throw new BuildException('No match expression specified.');
        }
        if ($this->replace === null && $this->match === null) {
            throw new BuildException(
                'You must specify either a preg_replace or preg_match pattern'
            );
        }
    }

    /**
     * @throws BuildException
     */
    public function main()
    {
        $this->validate();

        $output = $this->match;

        if ($this->replace !== null) {
            $output = $this->doReplace();
        } else {
            $output = $this->doSelect();
        }

        if ($output !== null) {
            $this->setPropertyValue($output);
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * ReplaceRegExp is a directory based task for replacing the occurrence of a
 * given regular expression with a substitution pattern in a selected file or
 * set of files.
 *
 * <code>
 * <replaceregexp file="${src}/build.properties"
 *                        match="OldProperty=(.*)"
 *                        replace="NewProperty=\1"
 *                        byline="true"/>
 * </code>
 *
 * @author    Jonathan Bond-Caron <jbondc@openmv.com>
 *
 * @package   phing.tasks.system
 *
 * @link      http://ant.apache.org/manual/OptionalTasks/replaceregexp.html
 */
class ReplaceRegexpTask extends Task
{
    /** Single file to process. */
    private $file;

    /**
     * Any filesets that should be processed.
     *
     * @var array $filesets
     */
    private $filesets = array();

    /**
     * Regular expression
     *
     * @var RegularExpression
     */
    private $_regexp;

    /**
     * File to apply regexp on
     *
     * @param PhingFile $path
     *
     * @return void
     */
    public function setFile(PhingFile $path)
    {
        $this->file = $path;
    }

    /**
     * Sets the regexp match pattern
     *
     * @param string $regexp
     *
     * @return void
     */
    public function setMatch($regexp)
    {
        $this->_regexp->setPattern($regexp);
    }

    /**
     * @see setMatch()
     *
     * @param $regexp
     *
     * @return void
     */
    public function setPattern($regexp)
    {
        $this->setMatch($regexp);
    }

    /**
     * Sets the replacement string
     *
     * @param string $string
     *
     * @return void
     */
    public function setReplace($string)
    {
        $this->_regexp->setReplace($string);
    }

    /**
     * Sets the regexp flags
     *
     * @param string $flags
     *
     * @return void
     *
     * todo ... `$this->_regexp->setFlags( $flags );`
     */
    public function setFlags($flags)
    {
    }

    /**
     * Match only per line
     *
     * @param bool $yesNo
     *
     * @return void
     */
    public function setByline($yesNo)
    {
        // TODO... $this->_regexp->
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     *
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * {@inheritdoc}
     *
     * @return void
     */
    public function init()
    {
        $this->_regexp = new RegularExpression();
    }

    /**
     * {@inheritdoc}
     *
     * @return void
     *
     * @throws BuildException
     */
    public function main()
    {
        if ($this->file === null && empty($this->filesets)) {
            throw new BuildException("You must specify a file or fileset(s) for the <ReplaceRegexp> task.");
        }

        // compile a list of all files to modify, both file attrib and fileset elements
        // can be used.
        $files = array();

        if ($this->file !== null) {
            $files[] = $this->file;
        }

        if (!empty($this->filesets)) {
            $filenames = array();
            foreach ($this->filesets as $fs) {
                try {
                    $ds = $fs->getDirectoryScanner($this->project);
                    $filenames = $ds->getIncludedFiles(); // get included filenames
                    $dir = $fs->getDir($this->project);
                    foreach ($filenames as $fname) {
                        $files[] = new PhingFile($dir, $fname);
                    }
                } catch (BuildException $be) {
                    $this->log($be->getMessage(), Project::MSG_WARN);
                }
            }
        }

        $this->log("Applying Regexp processing to " . count($files) . " files.");

        // These "slots" allow filters to retrieve information about the currently-being-process files
        $slot = $this->getRegisterSlot("currentFile");
        $basenameSlot = $this->getRegisterSlot("currentFile.basename");

        $filter = new FilterChain($this->project);

        $r = new ReplaceRegexp();
        $r->setRegexps(array($this->_regexp));

        $filter->addReplaceRegexp($r);
        $filters = array($filter);

        foreach ($files as $file) {
            // set the register slots

            $slot->setValue($file->getPath());
            $basenameSlot->setValue($file->getName());

            // 1) read contents of file, pulling through any filters
            $in = null;
            try {
                $contents = "";
                $in = FileUtils::getChainedReader(new FileReader($file), $filters, $this->project);
                while (-1 !== ($buffer = $in->read())) {
                    $contents .= $buffer;
                }
                $in->close();
            } catch (Exception $e) {
                if ($in) {
                    $in->close();
                }
                $this->log("Error reading file: " . $e->getMessage(), Project::MSG_WARN);
            }

            try {
                // now create a FileWriter w/ the same file, and write to the file
                $out = new FileWriter($file);
                $out->write($contents);
                $out->close();
                $this->log("Applying regexp processing to " . $file->getPath(), Project::MSG_VERBOSE);
            } catch (Exception $e) {
                if ($out) {
                    $out->close();
                }
                $this->log("Error writing file back: " . $e->getMessage(), Project::MSG_WARN);
            }

        }

    }
}
<?php

/**
 * reStructuredText rendering task for Phing, the PHP build tool.
 *
 * PHP version 5
 *
 * @category   Tasks
 * @package    phing.tasks.ext
 * @author     Christian Weiske <cweiske@cweiske.de>
 * @license    LGPL v3 or later http://www.gnu.org/licenses/lgpl.html
 * @link       http://www.phing.info/
 * @version    SVN: $Id: 5b4d68c019e0f3f6f497b8ec7c268c5600116ba0 $
 */

require_once 'phing/Task.php';
require_once 'phing/util/FileUtils.php';

/**
 * reStructuredText rendering task for Phing, the PHP build tool.
 *
 * PHP version 5
 *
 * @category   Tasks
 * @package    phing.tasks.ext
 * @author     Christian Weiske <cweiske@cweiske.de>
 * @license    LGPL v3 or later http://www.gnu.org/licenses/lgpl.html
 * @link       http://www.phing.info/
 */
class rSTTask extends Task
{
    /**
     * @var string Taskname for logger
     */
    protected $taskName = 'rST';

    /**
     * Result format, defaults to "html".
     * @see $supportedFormats for all possible options
     *
     * @var string
     */
    protected $format = 'html';

    /**
     * Array of supported output formats
     *
     * @var array
     * @see $format
     * @see $targetExt
     */
    protected static $supportedFormats = array(
        'html',
        'latex',
        'man',
        'odt',
        's5',
        'xml'
    );

    /**
     * Maps formats to file extensions
     *
     * @var array
     */
    protected static $targetExt = array(
        'html' => 'html',
        'latex' => 'tex',
        'man' => '3',
        'odt' => 'odt',
        's5' => 'html',
        'xml' => 'xml',
    );

    /**
     * Input file in rST format.
     * Required
     *
     * @var string
     */
    protected $file = null;

    /**
     * Additional rst2* tool parameters.
     *
     * @var string
     */
    protected $toolParam = null;

    /**
     * Full path to the tool, i.e. /usr/local/bin/rst2html
     *
     * @var string
     */
    protected $toolPath = null;

    /**
     * Output file or directory. May be omitted.
     * When it ends with a slash, it is considered to be a directory
     *
     * @var string
     */
    protected $destination = null;

    protected $filesets = array(); // all fileset objects assigned to this task
    protected $mapperElement = null;

    /**
     * all filterchains objects assigned to this task
     *
     * @var array
     */
    protected $filterChains = array();

    /**
     * mode to create directories with
     *
     * @var integer
     */
    protected $mode = 0;

    /**
     * Only render files whole source files are newer than the
     * target files
     *
     * @var boolean
     */
    protected $uptodate = false;

    /**
     * Sets up this object internal stuff. i.e. the default mode.
     */
    public function __construct()
    {
        $this->mode = 0777 - umask();
    }

    /**
     * Init method: requires the PEAR System class
     */
    public function init()
    {
        require_once 'System.php';
    }

    /**
     * The main entry point method.
     *
     * @throws BuildException
     * @return void
     */
    public function main()
    {
        $tool = $this->getToolPath($this->format);
        if (count($this->filterChains)) {
            $this->fileUtils = new FileUtils();
        }

        if ($this->file != '') {
            $file = $this->file;
            $targetFile = $this->getTargetFile($file, $this->destination);
            $this->render($tool, $file, $targetFile);

            return;
        }

        if (!count($this->filesets)) {
            throw new BuildException(
                '"file" attribute or "fileset" subtag required'
            );
        }

        // process filesets
        $mapper = null;
        if ($this->mapperElement !== null) {
            $mapper = $this->mapperElement->getImplementation();
        }

        $project = $this->getProject();
        foreach ($this->filesets as $fs) {
            $ds = $fs->getDirectoryScanner($project);
            $fromDir = $fs->getDir($project);
            $srcFiles = $ds->getIncludedFiles();

            foreach ($srcFiles as $src) {
                $file = new PhingFile($fromDir, $src);
                if ($mapper !== null) {
                    $results = $mapper->main($file);
                    if ($results === null) {
                        throw new BuildException(
                            sprintf(
                                'No filename mapper found for "%s"',
                                $file
                            )
                        );
                    }
                    $targetFile = reset($results);
                } else {
                    $targetFile = $this->getTargetFile($file, $this->destination);
                }
                $this->render($tool, $file, $targetFile);
            }
        }
    }

    /**
     * Renders a single file and applies filters on it
     *
     * @param string $tool       conversion tool to use
     * @param string $source     rST source file
     * @param string $targetFile target file name
     *
     * @return void
     */
    protected function render($tool, $source, $targetFile)
    {
        if (count($this->filterChains) == 0) {
            return $this->renderFile($tool, $source, $targetFile);
        }

        $tmpTarget = tempnam(sys_get_temp_dir(), 'rST-');
        $this->renderFile($tool, $source, $tmpTarget);

        $this->fileUtils->copyFile(
            new PhingFile($tmpTarget),
            new PhingFile($targetFile),
            true,
            false,
            $this->filterChains,
            $this->getProject(),
            $this->mode
        );
        unlink($tmpTarget);
    }

    /**
     * Renders a single file with the rST tool.
     *
     * @param string $tool       conversion tool to use
     * @param string $source     rST source file
     * @param string $targetFile target file name
     *
     * @return void
     *
     * @throws BuildException When the conversion fails
     */
    protected function renderFile($tool, $source, $targetFile)
    {
        if ($this->uptodate && file_exists($targetFile)
            && filemtime($source) <= filemtime($targetFile)
        ) {
            //target is up to date
            return;
        }
        //work around a bug in php by replacing /./ with /
        $targetDir = str_replace('/./', '/', dirname($targetFile));
        if (!is_dir($targetDir)) {
            $this->log("Creating directory '$targetDir'", Project::MSG_VERBOSE);
            mkdir($targetDir, $this->mode, true);
        }

        $cmd = $tool
            . ' --exit-status=2'
            . ' ' . $this->toolParam
            . ' ' . escapeshellarg($source)
            . ' ' . escapeshellarg($targetFile)
            . ' 2>&1';

        $this->log('command: ' . $cmd, Project::MSG_VERBOSE);
        exec($cmd, $arOutput, $retval);
        if ($retval != 0) {
            $this->log(implode("\n", $arOutput), Project::MSG_INFO);
            throw new BuildException('Rendering rST failed');
        }
        $this->log(implode("\n", $arOutput), Project::MSG_DEBUG);
    }

    /**
     * Finds the rst2* binary path
     *
     * @param string $format Output format
     *
     * @return string Full path to rst2$format
     *
     * @throws BuildException When the tool cannot be found
     */
    protected function getToolPath($format)
    {
        if ($this->toolPath !== null) {
            return $this->toolPath;
        }

        $tool = 'rst2' . $format;
        $path = System::which($tool);
        if (!$path) {
            throw new BuildException(
                sprintf('"%s" not found. Install python-docutils.', $tool)
            );
        }

        return $path;
    }

    /**
     * Determines and returns the target file name from the
     * input file and the configured destination name.
     *
     * @param string $file        Input file
     * @param string $destination Destination file or directory name,
     *                            may be null
     *
     * @return string Target file name
     *
     * @uses $format
     * @uses $targetExt
     */
    public function getTargetFile($file, $destination = null)
    {
        if ($destination != ''
            && substr($destination, -1) !== '/'
            && substr($destination, -1) !== '\\'
        ) {
            return $destination;
        }

        if (strtolower(substr($file, -4)) == '.rst') {
            $file = substr($file, 0, -4);
        }

        return $destination . $file . '.' . self::$targetExt[$this->format];
    }

    /**
     * The setter for the attribute "file"
     *
     * @param string $file Path of file to render
     *
     * @return void
     */
    public function setFile($file)
    {
        $this->file = $file;
    }

    /**
     * The setter for the attribute "format"
     *
     * @param string $format Output format
     *
     * @return void
     *
     * @throws BuildException When the format is not supported
     */
    public function setFormat($format)
    {
        if (!in_array($format, self::$supportedFormats)) {
            throw new BuildException(
                sprintf(
                    'Invalid output format "%s", allowed are: %s',
                    $format,
                    implode(', ', self::$supportedFormats)
                )
            );
        }
        $this->format = $format;
    }

    /**
     * The setter for the attribute "destination"
     *
     * @param string $destination Output file or directory. When it ends
     *                            with a slash, it is taken as directory.
     *
     * @return void
     */
    public function setDestination($destination)
    {
        $this->destination = $destination;
    }

    /**
     * The setter for the attribute "toolparam"
     *
     * @param string $param Additional rst2* tool parameters
     *
     * @return void
     */
    public function setToolparam($param)
    {
        $this->toolParam = $param;
    }

    /**
     * The setter for the attribute "toolpath"
     *
     * @param $path
     * @throws BuildException
     * @internal param string $param Full path to tool path, i.e. /usr/local/bin/rst2html
     *
     * @return void
     *
     */
    public function setToolpath($path)
    {
        if (!file_exists($path)) {
            $fullpath = System::which($path);
            if ($fullpath === false) {
                throw new BuildException(
                    'Tool does not exist. Path: ' . $path
                );
            }
            $path = $fullpath;
        }
        if (!is_executable($path)) {
            throw new BuildException(
                'Tool not executable. Path: ' . $path
            );
        }
        $this->toolPath = $path;
    }

    /**
     * The setter for the attribute "uptodate"
     *
     * @param string $uptodate True/false
     *
     * @return void
     */
    public function setUptodate($uptodate)
    {
        $this->uptodate = (boolean) $uptodate;
    }

    /**
     * Add a set of files to be rendered.
     *
     * @param FileSet $fileset Set of rst files to render
     *
     * @return void
     */
    public function addFileset(FileSet $fileset)
    {
        $this->filesets[] = $fileset;
    }

    /**
     * Nested creator, creates one Mapper for this task
     *
     * @return Mapper The created Mapper type object
     *
     * @throws BuildException
     */
    public function createMapper()
    {
        if ($this->mapperElement !== null) {
            throw new BuildException(
                'Cannot define more than one mapper', $this->location
            );
        }
        $this->mapperElement = new Mapper($this->project);

        return $this->mapperElement;
    }

    /**
     * Creates a filterchain, stores and returns it
     *
     * @return FilterChain The created filterchain object
     */
    public function createFilterChain()
    {
        $num = array_push($this->filterChains, new FilterChain($this->project));

        return $this->filterChains[$num - 1];
    }
}
<?php
/*
 *  $Id: eb75e99660cf6fe49f68fe568e00aca4a193e31a $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Paul Stuart <pstuart2@gmail.com>
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 */

/**
 * Pull in Task class.
 */
require_once 'phing/Task.php';

/**
 * Executes Sass for a particular fileset.
 *
 * If the sass executable is not available, but scssphp is, then use that instead.
 *
 * @category Tasks
 * @package  phing.tasks.ext
 * @author   Paul Stuart <pstuart2@gmail.com>
 * @author   Ken Guest <kguest@php.net>
 * @license  LGPL (see http://www.gnu.org/licenses/lgpl.html)
 * @version  Release: $Id: eb75e99660cf6fe49f68fe568e00aca4a193e31a $
 * @link     SassTask.php
 */
class SassTask extends Task
{

    /**
     * Style to generate to.
     *
     * @var string
     */
    protected $style = 'nested';

    /**
     * Stack trace on error.
     *
     * @var bool
     */
    protected $trace = false;

    /**
     * Unix-style newlines?
     *
     * @var bool
     */
    protected $unixnewlines = true;

    /**
     * Encoding
     *
     * @var string
     */
    protected $encoding = 'utf-8';

    /**
     * SASS import path.
     *
     * @var string
     */
    protected $loadPath = '';

    /**
     * Whether to just check syntax
     *
     * @var bool
     */
    protected $check = false;

    /**
     * Whether to use the sass command line tool.
     *
     * @var bool
     */
    protected $useSass = true;

    /**
     * Whether to use the scssphp compiler, if available.
     *
     * @var bool
     */
    protected $useScssphp = true;

    /**
     * Input filename if only processing one file is required.
     *
     * @var string|null
     */
    protected $file = null;

    /**
     * Output filename
     *
     * @var string|null
     */
    protected $output = null;

    /**
     * Scssphp compiler
     *
     * @var object
     */
    protected $scssCompiler = null;

    /**
     * Contains the path info of our file to allow us to parse.
     *
     * @var array
     */
    protected $pathInfo = null;

    /**
     * The Sass executable.
     *
     * @var string
     */
    protected $executable = 'sass';

    /**
     * The ext type we are looking for when Verifyext is set to true.
     *
     * More than likely should be "scss" or "sass".
     *
     * @var string
     */
    protected $extfilter = '';

    /**
     * This flag means 'note errors to the output, but keep going'
     *
     * @var bool
     */
    protected $failonerror = true;

    /**
     * The fileset we will be running Sass on.
     *
     * @var array
     */
    protected $filesets = array();

    /**
     * Additional flags to pass to sass.
     *
     * @var string
     */
    protected $flags = '';

    /**
     * Indicates if we want to keep the directory structure of the files.
     *
     * @var bool
     */
    protected $keepsubdirectories = true;

    /**
     * When true we will remove the current file ext.
     *
     * @var bool
     */
    protected $removeoldext = true;

    /**
     * The new ext our files will have.
     *
     * @var string
     */
    protected $newext = 'css';
    /**
     * The path to send our output files to.
     *
     * If not defined they will be created in the same directory the
     * input is from.
     *
     * @var string
     */
    protected $outputpath = '';

    /**
     * Set input file (For example style.scss)
     *
     * Synonym for @see setFile
     *
     * @param string $file Filename
     *
     * @return void
     */
    public function setInput($file)
    {
        $this->setFile($file);
    }

    /**
     * Set name of output file.
     *
     * @param string $file Filename of [css] to output.
     *
     * @return void
     */
    public function setOutput($file)
    {
        $this->output = $file;
    }

    /**
     * Sets the failonerror flag. Default: true
     *
     * @param string $failonerror Jenkins style boolean value
     *
     * @access public
     * @return void
     */
    public function setFailonerror($failonerror)
    {
        $this->failonerror = StringHelper::booleanValue($failonerror);
    }

    /**
     * Sets the executable to use for sass. Default: sass
     *
     * The default assumes sass is in your path. If not you can provide the full
     * path to sass.
     *
     * @param string $executable Name/path of sass executable
     *
     * @access public
     * @return void
     */
    public function setExecutable($executable)
    {
        $this->executable = $executable;
    }

    /**
     * Return name/path of sass executable.
     *
     * @return string
     */
    public function getExecutable()
    {
        return $this->executable;
    }

    /**
     * Sets the extfilter. Default: <none>
     *
     * This will filter the fileset to only process files that match
     * this extension. This could also be done with the fileset.
     *
     * @param string $extfilter Extension to filter for.
     *
     * @access public
     * @return void
     */
    public function setExtfilter($extfilter)
    {
        $this->extfilter = trim($extfilter, ' .');
    }

    /**
     * Return extfilter setting.
     *
     * @return string
     */
    public function getExtfilter()
    {
        return $this->extfilter;
    }

    /**
     * Additional flags to pass to sass.
     *
     * Command will be:
     * sass {$flags} {$inputfile} {$outputfile}
     *
     * @param string $flags List of flags accepted by sass.
     *
     * @access public
     * @return void
     */
    public function setFlags($flags)
    {
        $this->flags = trim($flags);
    }

    /**
     * Return flags to be used when running the sass executable.
     *
     * @return string
     */
    public function getFlags()
    {
        return trim($this->flags);
    }

    /**
     * Sets the removeoldext flag. Default: true
     *
     * This will cause us to strip the existing extension off the output
     * file.
     *
     * @param string $removeoldext Jenkins style boolean value
     *
     * @access public
     * @return void
     */
    public function setRemoveoldext($removeoldext)
    {
        $this->removeoldext = StringHelper::booleanValue($removeoldext);
    }

    /**
     * Return removeoldext value (true/false)
     *
     * @return bool
     */
    public function getRemoveoldext()
    {
        return $this->removeoldext;
    }

    /**
     * Set default encoding
     *
     * @param string $encoding Default encoding to use.
     *
     * @return void
     */
    public function setEncoding($encoding)
    {
        $encoding = trim($encoding);
        if ($encoding !== '') {
            $this->flags .= " --default-encoding $encoding";
        } else {
            $this->flags = str_replace(
                ' --default-encoding ' . $this->encoding,
                '',
                $this->flags
            );
        }
        $this->encoding = $encoding;
    }

    /**
     * Return the output encoding.
     *
     * @return string
     */
    public function getEncoding()
    {
        return $this->encoding;
    }

    /**
     * Sets the newext value. Default: css
     *
     * This is the extension we will add on to the output file regardless
     * of if we remove the old one or not.
     *
     * @param string $newext New extension to use, e.g. css
     *
     * @access public
     * @return void
     */
    public function setNewext($newext)
    {
        $this->newext = trim($newext, ' .');
    }

    /**
     * Return extension added to output files.
     *
     * @return string
     */
    public function getNewext()
    {
        return $this->newext;
    }

    /**
     * Sets the outputpath value. Default: <none>
     *
     * This will force the output path to be something other than
     * the path of the fileset used.
     *
     * @param string $outputpath Path name
     *
     * @access public
     * @return void
     */
    public function setOutputpath($outputpath)
    {
        $this->outputpath = rtrim(trim($outputpath), DIRECTORY_SEPARATOR);
    }

    /**
     * Return the outputpath value.
     *
     * @return string
     */
    public function getOutputpath()
    {
        return $this->outputpath;
    }

    /**
     * Sets the keepsubdirectories value. Default: true
     *
     * When set to true we will keep the directory structure. So any input
     * files in subdirectories will have their output file in that same
     * sub-directory. If false, all output files will be put in the path
     * defined by outputpath or in the directory top directory of the fileset.
     *
     * @param bool $keepsubdirectories Jenkins style boolean
     *
     * @access public
     * @return void
     */
    public function setKeepsubdirectories($keepsubdirectories)
    {
        $this->keepsubdirectories = StringHelper::booleanValue($keepsubdirectories);
    }

    /**
     * Return keepsubdirectories value.
     *
     * @return bool
     */
    public function getKeepsubdirectories()
    {
        return $this->keepsubdirectories;
    }

    /**
     * Nested creator, creates a FileSet for this task
     *
     * @return FileSet The created fileset object
     */
    public function createFileSet()
    {
        $num = array_push($this->filesets, new FileSet());
        return $this->filesets[$num - 1];
    }

    /**
     * Whether to just check syntax.
     *
     * @param string $value Jenkins style boolean value
     *
     * @return void
     */
    public function setCheck($value)
    {
        $check = StringHelper::booleanValue($value);
        $this->check = $check;
        if ($check) {
            $this->flags .= ' --check ';
        } else {
            $this->flags = str_replace(' --check ', '', $this->flags);
        }
    }

    /**
     * Indicate if just a syntax check is required.
     *
     * @return boolean
     */
    public function getCheck()
    {
        return $this->check;
    }

    /**
     * Set style to compact
     *
     * @param string $value Jenkins style boolean value
     *
     * @return void
     */
    public function setCompact($value)
    {
        $compress = StringHelper::booleanValue($value);
        if ($compress) {
            $this->flags = str_replace(' --style ' . $this->style, '', $this->flags);
            $this->flags .= ' --style compact';
            $this->style = 'compact';
        }
    }

    /**
     * Indicate whether style is set to 'coompact'.
     *
     * @return bool
     * @see    setCompact
     */
    public function getCompact()
    {
        return $this->style === 'compact';
    }

    /**
     * Set style to compressed
     *
     * @param string $value Jenkins style boolean value
     *
     * @return void
     */
    public function setCompressed($value)
    {
        $compress = StringHelper::booleanValue($value);
        if ($compress) {
            $this->flags = str_replace(' --style ' . $this->style, '', $this->flags);
            $this->flags .= ' --style compressed';
            $this->style = 'compressed';
        }
    }

    /**
     * Indicate whether style is set to 'compressed'.
     *
     * @return bool
     * @see    setCompressed
     */
    public function getCompressed()
    {
        return $this->style === 'compressed';
    }

    /**
     * Set style to crunched. Supported by scssphp only.
     *
     * @param string $value Jenkins style boolean value
     *
     * @return void
     */
    public function setCrunched($value)
    {
        $compress = StringHelper::booleanValue($value);
        if ($compress) {
            $this->style = 'crunched';
        }
    }

    /**
     * Indicate whether style is set to 'crunched'.
     *
     * @return bool
     * @see    setCrunched
     */
    public function getCrunched()
    {
        return $this->style === 'crunched';
    }

    /**
     * Set style to expanded
     *
     * @param string $value Jenkins style boolean value
     *
     * @return void
     */
    public function setExpand($value)
    {
        $expand = StringHelper::booleanValue($value);
        if ($expand) {
            $this->flags = str_replace(' --style ' . $this->style, '', $this->flags);
            $this->flags .= ' --style expanded';
            $this->style = 'expanded';
        }
    }

    /**
     * Indicate whether style is set to 'expanded'.
     *
     * @return bool
     * @see    setExpand
     */
    public function getExpand()
    {
        return $this->style === 'expanded';
    }

    /**
     * Set style to nested
     *
     * @param string $value Jenkins style boolean value
     *
     * @return void
     */
    public function setNested($value)
    {
        $nested = StringHelper::booleanValue($value);
        if ($nested) {
            $this->flags = str_replace(' --style ' . $this->style, '', $this->flags);
            $this->flags .= ' --style nested';
            $this->style = 'nested';
        }
    }

    /**
     * Indicate whether style is set to 'nested'.
     *
     * @return bool
     * @see    setNested
     */
    public function getNested()
    {
        return $this->style === 'nested';
    }

    /**
     * Whether to force recompiled when --update is used.
     *
     * @param string $value Jenkins style boolean value
     *
     * @return void
     */
    public function setForce($value)
    {
        $force = StringHelper::booleanValue($value);
        $this->force = $force;
        if ($force) {
            $this->flags .= ' --force ';
        } else {
            $this->flags = str_replace(' --force ', '', $this->flags);
        }
    }

    /**
     * Return force value.
     *
     * @return bool
     */
    public function getForce()
    {
        return $this->force;
    }

    /**
     * Whether to cache parsed sass files.
     *
     * @param string $value Jenkins style boolean value
     *
     * @return void
     */
    public function setNoCache($value)
    {
        $noCache = StringHelper::booleanValue($value);
        $this->noCache = $noCache;
        if ($noCache) {
            $this->flags .= ' --no-cache ';
        } else {
            $this->flags = str_replace(' --no-cache ', '', $this->flags);
        }
    }

    /**
     * Return noCache value.
     *
     * @return bool
     */
    public function getNoCache()
    {
        return $this->noCache;
    }

    /**
     * Specify SASS import path
     *
     * @param string $path Import path.
     *
     * @return void
     */
    public function setPath($path)
    {
        $this->flags .= "--load-path $path";
        $this->loadPath = $path;
    }

    /**
     * Return the SASS import path.
     *
     * @return string
     */
    public function getPath()
    {
        return $this->loadPath;
    }

    /**
     * Set output style.
     *
     * @param mixed $style Style.
     *
     * @return void
     */
    public function setStyle($style)
    {
        $style = strtolower($style);
        switch($style) {
        case 'nested':
        case 'compact':
        case 'compressed':
        case 'expanded':
        case 'crunched':
            $this->flags = str_replace(" --style $this->style", '', $this->flags);
            $this->style = $style;
            $this->flags .= " --style $style";
            break;
        default:
            $this->log("Style $style ignored", Project::MSG_INFO);
        }
    }

    /**
     * Return style used for generating output.
     *
     * @return string
     */
    public function getStyle()
    {
        return $this->style;
    }

    /**
     * Set trace option.
     *
     * IE: Whether to output a stack trace on error.
     *
     * @param string $trace Jenkins style boolean value
     *
     * @return void
     */
    public function setTrace($trace)
    {
        $this->trace = StringHelper::booleanValue($trace);
        if ($this->trace) {
            $this->flags .= ' --trace ';
        } else {
            $this->flags = str_replace(' --trace ', '', $this->flags);
        }
    }

    /**
     * Return trace option.
     *
     * @return bool
     */
    public function getTrace()
    {
        return $this->trace;
    }

    /**
     * Whether to use unix-style newlines.
     *
     * @param string $newlines Jenkins style boolean value
     *
     * @return void
     */
    public function setUnixnewlines($newlines)
    {
        $unixnewlines = StringHelper::booleanValue($newlines);
        $this->unixnewlines = $unixnewlines;
        if ($unixnewlines) {
            $this->flags .= ' --unix-newlines ';
        } else {
            $this->flags = str_replace(' --unix-newlines ', '', $this->flags);
        }
    }

    /**
     * Return unix-newlines setting
     *
     * @return bool
     */
    public function getUnixnewlines()
    {
        return $this->unixnewlines;
    }

    /**
     * Whether to identify source-file and line number for generated CSS.
     *
     * @param string $lineNumbers Jenkins style boolean value
     *
     * @return void
     */
    public function setLineNumbers($lineNumbers)
    {
        $lineNumbers = StringHelper::booleanValue($lineNumbers);
        $this->lineNumbers = $lineNumbers;
        if ($lineNumbers) {
            $this->flags .= ' --line-numbers ';
        } else {
            $this->flags = str_replace(' --line-numbers ', '', $this->flags);
        }
    }

    /**
     * Return line-numbers setting.
     *
     * @return bool
     */
    public function getLineNumbers()
    {
        return $this->lineNumbers;
    }

    /**
     * Whether to use the 'sass' command line tool.
     *
     * @param string $value Jenkins style boolean value.
     *
     * @return void
     * @link   http://sass-lang.com/install
     */
    public function setUseSass($value)
    {
        $this->useSass = StringHelper::booleanValue($value);
    }

    /**
     * Whether to use the scssphp compiler.
     *
     * @param string $value Jenkins style boolean value.
     *
     * @return void
     * @link   http://leafo.github.io/scssphp/
     */
    public function setUseScssphp($value)
    {
        $this->useScssphp = StringHelper::booleanValue($value);
        if (version_compare(PHP_VERSION, '5.2', '<=')) {
            $this->useScssphp = false;
            $this->log(
                "SCSSPHP is incompatible with this version of PHP",
                Project::MSG_INFO
            );
        }
    }

    /**
     * Set single filename to compile from scss to css.
     *
     * @param string $file Single filename to compile.
     *
     * @return void
     */
    public function setFile($file)
    {
        $this->file = $file;
    }

    /**
     * Init: pull in the PEAR System class
     *
     * @access public
     * @return void
     */
    public function init()
    {
        @include_once 'System.php';
        if (!class_exists('System')) {
            throw new BuildException("You must have installed PEAR in order to use SassTask.");
        }
        @include_once 'vendor/autoload.php';
        if (version_compare(PHP_VERSION, '5.2', '<=')) {
            $this->useScssphp = false;
            $this->log(
                "SCSSPHP is incompatible with this version of PHP",
                Project::MSG_INFO
            );
        }
    }

    /**
     * Our main execution of the task.
     *
     * @throws BuildException
     * @throws Exception
     *
     * @access public
     * @return void
     */
    public function main()
    {
        if ($this->useSass) {
            if (strlen($this->executable) < 0) {
                throw new BuildException("'executable' must be defined.");
            }
        }

        if (empty($this->filesets) && $this->file === null) {
            throw new BuildException(
                "Missing either a nested fileset or attribute 'file'"
            );
        }

        // If both are set to be used, prefer sass over scssphp.
        $lUseScssphp = false;
        if ($this->useSass && $this->useScssphp) {
            if (System::which($this->executable) === false) {
                if ($this->loadScssphp() === false) {
                    $msg = sprintf(
                        "%s not found. Install sass or leafo scssphp.",
                        $this->executable
                    );
                    if ($this->failonerror) {
                        throw new BuildException($msg);
                    } else {
                        $this->log($msg, Project::MSG_ERR);
                        return;
                    }
                } else {
                    $lUseScssphp = true;
                    $this->scssCompiler = $this->initialiseScssphp();
                }
            }
        } elseif (!$this->useSass && !$this->useScssphp) {
            $this->log(
                "Neither sass nor scssphp are to be used.",
                Project::MSG_ERR
            );
            return;
        } elseif ($this->useSass) {
            if (System::which($this->executable) === false) {
                $msg = sprintf(
                    "%s not found. Install sass.",
                    $this->executable
                );
                if ($this->failonerror) {
                    throw new BuildException($msg);
                } else {
                    $this->log($msg, Project::MSG_ERR);
                    return;
                }
            }
        } elseif ($this->useScssphp) {
            if ($this->loadScssphp() === false) {
                $msg = sprintf(
                    "Install leafo scssphp."
                );
                if ($this->failonerror) {
                    throw new BuildException($msg);
                } else {
                    $this->log($msg, Project::MSG_ERR);
                    return;
                }
            } else {
                $lUseScssphp = true;
                $this->scssCompiler = $this->initialiseScssphp();
            }
        }

        if (count($this->filesets) > 0) {
            $this->processFilesets($lUseScssphp);
        } elseif ($this->file !== null) {
            $this->processFile($lUseScssphp);
        }
    }

    /**
     * Compile a specified file.
     *
     * If output file is not specified, but outputpath is, place output in
     * that directory. If neither is specified, place .css file in the
     * directory that the input file is in.
     *
     * @param boolean $useScssphp Whether to use the scssphp compiler.
     *
     * @return void
     */
    public function processFile($useScssphp)
    {
        $this->log("Process file", Project::MSG_INFO);
        if (is_null($this->output)) {
            $specifiedOutputPath = (strlen($this->outputpath) > 0);
            if ($specifiedOutputPath === false) {
                $info = pathinfo($this->file);
                $path = $info['dirname'];
                $this->outputpath = $path;
            } else {
                $path = $this->outputpath;
            }
            $output = $path . DIRECTORY_SEPARATOR . $info['filename'];
            if (!$this->removeoldext) {
                $output .= '.' . $this->pathInfo['extension'];
            }

            if (strlen($this->newext) > 0) {
                $output .= '.' . $this->newext;
            }
            $this->output = $output;
        } else {
            $output = $this->output;
        }

        $this->compile($this->file, $output, $useScssphp);
    }

    /**
     * Process filesets - compiling/generating css files as required.
     *
     * @param boolean $useScssphp Whether to use the scssphp compiler.
     *
     * @return void
     */
    public function processFilesets($useScssphp)
    {
        foreach ($this->filesets as $fs) {
            $ds = $fs->getDirectoryScanner($this->project);
            $files = $ds->getIncludedFiles();
            $dir = $fs->getDir($this->project)->getPath();

            // If output path isn't defined then set it to the path of our fileset.
            $specifiedOutputPath = (strlen($this->outputpath) > 0);
            if ($specifiedOutputPath === false) {
                $this->outputpath = $dir;
            }

            foreach ($files as $file) {
                $fullFilePath = $dir . DIRECTORY_SEPARATOR . $file;
                $this->pathInfo = pathinfo($file);

                $run = true;
                switch(strtolower($this->pathInfo['extension'])) {
                case 'scss':
                case 'sass':
                    break;
                default:
                    $this->log('Ignoring ' . $file, Project::MSG_DEBUG);
                    $run = false;
                }

                if ($run
                    && ($this->extfilter === ''
                    || $this->extfilter === $this->pathInfo['extension'])
                ) {
                    $outputFile = $this->buildOutputFilePath();
                    $this->compile($fullFilePath, $outputFile, $useScssphp);
                }
            }
        }
    }

    /**
     * Compile
     *
     * @param string  $fullFilePath Fully qualified filename to compile.
     * @param string  $outputFile   Filename for generated css.
     * @param boolean $lUseScssphp  Whether to use scssphp compiler.
     *
     * @return void
     */
    public function compile($fullFilePath, $outputFile, $lUseScssphp)
    {
        $output = null;
        if (!$lUseScssphp) {
            try {
                $output = $this->executeCommand(
                    $fullFilePath,
                    $outputFile
                );
                if ($this->failonerror && $output[0] !== 0) {
                    throw new BuildException(
                        "Result returned as not 0. Result: {$output[0]}",
                        Project::MSG_INFO
                    );
                }
            } catch (Exception $e) {
                if ($this->failonerror) {
                    throw $e;
                } else {
                    $this->log(
                        "Result: {$output[0]}",
                        Project::MSG_INFO
                    );
                }
            }
        } else {
            $this->log(
                sprintf("Compiling '%s' via scssphp", $fullFilePath),
                Project::MSG_INFO
            );
            $input = file_get_contents($fullFilePath);
            try {
                $out = $this->scssCompiler->compile($input, $fullFilePath);
                if ($out !== '') {
                    $success = file_put_contents($outputFile, $out);
                    if ($success) {
                        $this->log(
                            sprintf(
                                "'%s' compiled and written to '%s'",
                                $fullFilePath,
                                $outputFile
                            ),
                            Project::MSG_VERBOSE
                        );
                    }
                } else {
                    $this->log('Compilation resulted in empty string');
                }
            } catch (Exception $ex) {
                if ($this->failonerror) {
                    throw new BuildException($ex->getMessage());
                } else {
                    $this->log($ex->getMessage(), Project::MSG_ERR);
                }
            }
        }
    }

    /**
     * Builds the full path to the output file based on our settings.
     *
     * @return string
     *
     * @access protected
     */
    protected function buildOutputFilePath()
    {
        $outputFile = $this->outputpath.DIRECTORY_SEPARATOR;

        $subpath = trim($this->pathInfo['dirname'], ' .');

        if ($this->keepsubdirectories === true && strlen($subpath) > 0) {
            $outputFile .= $subpath . DIRECTORY_SEPARATOR;
        }

        $outputFile .= $this->pathInfo['filename'];

        if (!$this->removeoldext) {
            $outputFile .= '.' . $this->pathInfo['extension'];
        }

        if (strlen($this->newext) > 0) {
            $outputFile .= '.' . $this->newext;
        }

        return $outputFile;
    }

    /**
     * Executes the command and returns return code and output.
     *
     * @param string $inputFile  Input file
     * @param string $outputFile Output file
     *
     * @access protected
     * @throws BuildException
     * @return array array(return code, array with output)
     */
    protected function executeCommand($inputFile, $outputFile)
    {
        // Prevent over-writing existing file.
        if ($inputFile == $outputFile) {
            throw new BuildException('Input file and output file are the same!');
        }

        $output = array();
        $return = null;

        $fullCommand = $this->executable;

        if (strlen($this->flags) > 0) {
            $fullCommand .= " {$this->flags}";
        }

        $fullCommand .= " {$inputFile} {$outputFile}";

        $this->log("Executing: {$fullCommand}", Project::MSG_INFO);
        exec($fullCommand, $output, $return);

        return array($return, $output);
    }

    /**
     * Pull in scssphp package, return true if successful.
     *
     * @return bool
     */
    public function loadScssphp()
    {
        $success = @include_once "vendor/leafo/scssphp/scss.inc.php";
        if ($success === false) {
            $success = @include_once "scssphp/scss.inc.php";
        }
        return $success;
    }

    /**
     * Get ScssPhp Compiler.
     *
     * @return Leafo\ScssPhp\Compiler
     */
    public function getNewCompiler()
    {
        // Instantiate the class in a way that is compatible with
        // PHP 5.2 up to 7.x.
        $compiler = '\\Leafo\\ScssPhp\\Compiler';
        return new $compiler;
    }

    /**
     * Initialise and return an instance of the ScssPhp Compiler.
     *
     * @return Leafo\ScssPhp\Compiler
     */
    public function initialiseScssphp()
    {
        $scss = $this->getNewCompiler();
        if ($this->style) {
            $ucStyle = ucfirst(strtolower($this->style));
            $scss->setFormatter('Leafo\\ScssPhp\\Formatter\\' . $ucStyle);
        }
        if ($this->encoding) {
            $scss->setEncoding($this->encoding);
        }
        if ($this->lineNumbers) {
            $scss->setLineNumberStyle(1);
        }
        if ($this->loadPath !== '') {
            $scss->setImportPaths(explode(PATH_SEPARATOR, $this->loadPath));
        }
        return $scss;
    }
}
<?php

/*
 *  $Id: 755f7c3784838bc960dba9520a10490c640a61cd $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once dirname(dirname(__FILE__)) . '/S3.php';

/**
 * Downloads an object off S3
 *
 * @version $Id: 755f7c3784838bc960dba9520a10490c640a61cd $
 * @package phing.tasks.ext
 * @author Andrei Serdeliuc <andrei@serdeliuc.ro>
 * @extends Service_Amazon_S3
 */
class S3GetTask extends Service_Amazon_S3
{
    /**
     * This is where we'll store the object
     *
     * (default value: null)
     *
     * @var mixed
     */
    protected $_target = null;

    /**
     * The S3 object we're working with
     *
     * (default value: null)
     *
     * @var mixed
     */
    protected $_object = null;

    /**
     * @param $object
     * @throws BuildException
     */
    public function setObject($object)
    {
        if (empty($object) || !is_string($object)) {
            throw new BuildException('Object must be a non-empty string');
        }

        $this->_object = $object;
    }

    /**
     * @return mixed
     * @throws BuildException
     */
    public function getObject()
    {
        if ($this->_object === null) {
            throw new BuildException('Object is not set');
        }

        return $this->_object;
    }

    /**
     * @param $target
     * @throws BuildException
     */
    public function setTarget($target)
    {
        if (!is_file($target) && !is_dir($target) && !is_link($target)) {
            if (!is_writable(dirname($target))) {
                throw new BuildException('Target is not writable: ' . $target);
            }
        } else {
            if (!is_writable($target)) {
                throw new BuildException('Target is not writable: ' . $target);
            }
        }

        $this->_target = $target;
    }

    /**
     * @return mixed
     * @throws BuildException
     */
    public function getTarget()
    {
        if ($this->_target === null) {
            throw new BuildException('Target is not set');
        }

        return $this->_target;
    }

    public function execute()
    {
        $target = $this->getTarget();

        // Use the object name as the target if the current target is a directory
        if (is_dir($target)) {
            $target = rtrim($target, '/') . '/' . $this->getObject();
        }

        file_put_contents($target, $this->getObjectContents($this->getObject()));
    }
}
<?php
/*
 *  $Id: 37e9c57cc495b2eb764a5bbd6221a7d6ad115127 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once dirname(dirname(__FILE__)) . '/S3.php';

/**
 * Stores an object on S3
 *
 * @version $Id: 37e9c57cc495b2eb764a5bbd6221a7d6ad115127 $
 * @package phing.tasks.ext
 * @author Andrei Serdeliuc <andrei@serdeliuc.ro>
 * @extends Service_Amazon_S3
 */
class S3PutTask extends Service_Amazon_S3
{
    /**
     * File we're trying to upload
     *
     * (default value: null)
     *
     * @var string
     */
    protected $_source = null;

    /**
     * Content we're trying to upload
     *
     * The user can specify either a file to upload or just a bit of content
     *
     * (default value: null)
     *
     * @var mixed
     */
    protected $_content = null;

    /**
     * Collection of filesets
     * Used for uploading multiple files
     *
     * (default value: array())
     *
     * @var array
     */
    protected $_filesets = array();

    /**
     * Whether to try to create buckets or not
     *
     * (default value: false)
     *
     * @var bool
     */
    protected $_createBuckets = false;

    /**
     * File ACL
     * Use to set the permission to the uploaded files
     *
     * (default value: 'private')
     *
     * @var string
     */
    protected $_acl = 'private';

    /**
     * File content type
     * Use this to set the content type of your static files
     * Set contentType to "auto" if you want to autodetect the content type based on the source file extension
     *
     * (default value: 'binary/octet-stream')
     *
     * @var string
     */
    protected $_contentType = 'binary/octet-stream';

    /**
     * Object maxage (in seconds).
     *
     * @var int
     */
    protected $_maxage = null;

    /**
     * Content is gzipped.
     *
     * @var boolean
     */
    protected $_gzipped = false;

    /**
     * Extension content type mapper
     *
     * @var array
     */
    protected $_extensionContentTypeMapper = array(
        'js' => 'application/x-javascript',
        'css' => 'text/css',
        'html' => 'text/html',
        'gif' => 'image/gif',
        'png' => 'image/png',
        'jpg' => 'image/jpeg',
        'jpeg' => 'image/jpeg',
        'txt' => 'text/plain'
    );

    /**
     * Whether filenames contain paths
     *
     * (default value: false)
     *
     * @var bool
     */
    protected $_fileNameOnly = false;

    /**
     * @param $source
     * @throws BuildException
     */
    public function setSource($source)
    {
        if (!is_readable($source)) {
            throw new BuildException('Source is not readable: ' . $source);
        }

        $this->_source = $source;
    }

    /**
     * @return string
     * @throws BuildException
     */
    public function getSource()
    {
        if ($this->_source === null) {
            throw new BuildException('Source is not set');
        }

        return $this->_source;
    }

    /**
     * @param $content
     * @throws BuildException
     */
    public function setContent($content)
    {
        if (empty($content) || !is_string($content)) {
            throw new BuildException('Content must be a non-empty string');
        }

        $this->_content = $content;
    }

    /**
     * @return mixed
     * @throws BuildException
     */
    public function getContent()
    {
        if ($this->_content === null) {
            throw new BuildException('Content is not set');
        }

        return $this->_content;
    }

    /**
     * @param $object
     * @throws BuildException
     */
    public function setObject($object)
    {
        if (empty($object) || !is_string($object)) {
            throw new BuildException('Object must be a non-empty string');
        }

        $this->_object = $object;
    }

    public function getObject()
    {
        if ($this->_object === null) {
            throw new BuildException('Object is not set');
        }

        return $this->_object;
    }

    /**
     * @param $permission
     * @throws BuildException
     */
    public function setAcl($permission)
    {
        $valid_acl = array('private', 'public-read', 'public-read-write', 'authenticated-read');
        if (empty($permission) || !is_string($permission) || !in_array($permission, $valid_acl)) {
            throw new BuildException('Object must be one of the following values: ' . implode('|', $valid_acl));
        }
        $this->_acl = $permission;
    }

    /**
     * @return string
     */
    public function getAcl()
    {
        return $this->_acl;
    }

    /**
     * @param $contentType
     */
    public function setContentType($contentType)
    {
        $this->_contentType = $contentType;
    }

    /**
     * @return string
     * @throws BuildException
     */
    public function getContentType()
    {
        if ($this->_contentType === 'auto') {
            $ext = strtolower(substr(strrchr($this->getSource(), '.'), 1));
            if (isset($this->_extensionContentTypeMapper[$ext])) {
                return $this->_extensionContentTypeMapper[$ext];
            } else {
                return 'binary/octet-stream';
            }
        } else {
            return $this->_contentType;
        }
    }

    /**
     * @param $createBuckets
     */
    public function setCreateBuckets($createBuckets)
    {
        $this->_createBuckets = (bool) $createBuckets;
    }

    /**
     * @return bool
     */
    public function getCreateBuckets()
    {
        return (bool) $this->_createBuckets;
    }

    /**
     * Set seconds in max-age, null value exclude max-age setup.
     *
     * @param int $seconds
     */
    public function setMaxage($seconds)
    {
        $this->_maxage = $seconds;
    }

    /**
     * Get seconds in max-age or null.
     *
     * @return int
     *             Number of seconds in maxage or null.
     */
    public function getMaxage()
    {
        return $this->_maxage;
    }

    /**
     * Set if content is gzipped.
     *
     * @param boolean $gzipped
     */
    public function setGzip($gzipped)
    {
        $this->_gzipped = $gzipped;
    }

    /**
     * Return if content is gzipped.
     *
     * @return booleand
     *                  Indicate if content is gzipped.
     */
    public function getGzip()
    {
        return $this->_gzipped;
    }

    /**
     * Generate HTTPHEader array sent to S3.
     *
     * @return array
     *               HttpHeader to set in S3 Object.
     */
    protected function getHttpHeaders()
    {
        $headers = array();
        if (!is_null($this->_maxage)) {
            $headers['Cache-Control'] = 'max-age=' . $this->_maxage;
        }
        if ($this->_gzipped) {
            $headers['Content-Encoding'] = 'gzip';
        }

        return $headers;
    }

    /**
     * @param $fileNameOnly
     */
    public function setFileNameOnly($fileNameOnly)
    {
        $this->_fileNameOnly = (bool) $fileNameOnly;
    }

    /**
     * creator for _filesets
     *
     * @return FileSet
     */
    public function createFileset()
    {
        $num = array_push($this->_filesets, new FileSet());

        return $this->_filesets[$num - 1];
    }

    /**
     * getter for _filesets
     *
     * @return array
     */
    public function getFilesets()
    {
        return $this->_filesets;
    }

    /**
     * Determines what we're going to store in the object
     *
     * If _content has been set, this will get stored,
     * otherwise, we read from _source
     *
     * @throws BuildException
     * @return string
     */
    public function getObjectData()
    {
        try {
            $content = $this->getContent();
        } catch (BuildException $e) {
            $source = $this->getSource();

            if (!is_file($source)) {
                throw new BuildException('Currently only files can be used as source');
            }

            $content = file_get_contents($source);
        }

        return $content;
    }

    /**
     * Store the object on S3
     *
     * @throws BuildException
     * @return void
     */
    public function execute()
    {
        if (!$this->isBucketAvailable()) {
            if (!$this->getCreateBuckets()) {
                throw new BuildException('Bucket doesn\'t exist and createBuckets not specified');
            } else {
                if (!$this->createBucket()) {
                    throw new BuildException('Bucket cannot be created');
                }
            }
        }

        // Filesets take precedence
        if (!empty($this->_filesets)) {
            $objects = array();

            foreach ($this->_filesets as $fs) {
                if (!($fs instanceof FileSet)) {
                    continue;
                }

                $ds = $fs->getDirectoryScanner($this->getProject());
                $objects = array_merge($objects, $ds->getIncludedFiles());
            }

            $fromDir = $fs->getDir($this->getProject())->getAbsolutePath();

            if ($this->_fileNameOnly) {
                foreach ($objects as $object) {
                    $this->_source = $object;
                    $this->saveObject(basename($object), file_get_contents($fromDir . DIRECTORY_SEPARATOR . $object));
                }
            } else {
                foreach ($objects as $object) {
                    $this->_source = $object;
                    $this->saveObject(
                        str_replace('\\', '/', $object),
                        file_get_contents($fromDir . DIRECTORY_SEPARATOR . $object)
                    );
                }
            }

            return true;
        }

        $this->saveObject($this->getObject(), $this->getObjectData());
    }

    /**
     * @param $object
     * @param $data
     * @throws BuildException
     */
    protected function saveObject($object, $data)
    {
        $object = $this->getObjectInstance($object);
        $object->data = $data;
        $object->acl = $this->getAcl();
        $object->contentType = $this->getContentType();
        $object->httpHeaders = $this->getHttpHeaders();
        $object->save();

        if (!$this->isObjectAvailable($object->key)) {
            throw new BuildException('Upload failed');
        }
    }
}
<?php

/*
 *  $Id: c858af9a61ef05f61b1ae1c818a58ce7b0f3505a $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once dirname(dirname(__FILE__)) . "/Amazon.php";

/**
 * Abstract Service_Amazon_S3 class.
 *
 * Provides common methods and properties to all of the S3 tasks
 *
 * @version $ID$
 * @package phing.tasks.ext
 * @author Andrei Serdeliuc <andrei@serdeliuc.ro>
 */
abstract class Service_Amazon_S3 extends Service_Amazon
{
    /**
     * Services_Amazon_S3 client
     *
     * (default value: null)
     *
     * @var Services_Amazon_S3
     * @see Services_Amazon_S3
     */
    protected $_client = null;

    /**
     * We only instantiate the client once per task call
     *
     * @return Services_Amazon_S3
     */
    public function getClient()
    {
        require_once "Services/Amazon/S3.php";

        if ($this->_client === null) {
            $this->_client = Services_Amazon_S3::getAccount($this->getKey(), $this->getSecret());
        }

        return $this->_client;
    }

    /**
     * @param $bucket
     * @throws BuildException
     */
    public function setBucket($bucket)
    {
        if (empty($bucket) || !is_string($bucket)) {
            throw new BuildException('Bucket must be a non-empty string');
        }

        $this->bucket = (string) $bucket;
    }

    /**
     * @throws BuildException
     */
    public function getBucket()
    {
        if (!($bucket = $this->bucket)) {
            throw new BuildException('Bucket is not set');
        }

        return $this->bucket;
    }

    /**
     * Returns an instance of Services_Amazon_S3_Resource_Object
     *
     * @param  mixed                              $object
     * @return Services_Amazon_S3_Resource_Object
     */
    public function getObjectInstance($object)
    {
        return $this->getBucketInstance()->getObject($object);
    }

    /**
     * Check if the object already exists in the current bucket
     *
     * @param  mixed $object
     * @return bool
     */
    public function isObjectAvailable($object)
    {
        return (bool) $this->getObjectInstance($object)->load(Services_Amazon_S3_Resource_Object::LOAD_METADATA_ONLY);
    }

    /**
     * Returns an instance of Services_Amazon_S3_Resource_Bucket
     *
     * @return Services_Amazon_S3_Resource_Bucket
     */
    public function getBucketInstance()
    {
        return $this->getClient()->getBucket($this->getBucket());
    }

    /**
     * Check if the current bucket is available
     *
     * @return bool
     */
    public function isBucketAvailable()
    {
        return (bool) $this->getBucketInstance($this->getBucket())->load();
    }

    /**
     * Get the contents of an object (by it's name)
     *
     * @param  string $object
     * @throws BuildException
     * @return mixed
     */
    public function getObjectContents($object)
    {
        if (!$this->isBucketAvailable($this->getBucket())) {
            throw new BuildException('Bucket doesn\'t exist or wrong permissions');
        }

        $bucket = $this->getClient()->getBucket($this->getBucket());
        if (!$this->isObjectAvailable($object)) {
            throw new BuildException('Object not available: ' . $object);
        }

        $object = $this->getObjectInstance($object);
        $object->load();

        return $object->data;
    }

    /**
     * Create a bucket
     *
     * @return bool
     */
    public function createBucket()
    {
        $bucket = $this->getBucketInstance();
        $bucket->name = $this->getBucket();
        $bucket->save();

        return $this->isBucketAvailable();
    }

    /**
     * Main entry point, doesn't do anything
     *
     * @return void
     */
    final public function main()
    {
        $this->execute();
    }

    /**
     * Entry point to children tasks
     *
     * @return void
     */
    abstract public function execute();
}
<?php

/*
 *  $Id: d064a9ec403afcb58953d4531fd251c91fc15e20 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once "phing/Task.php";

/**
 * Abstract Service_Amazon class.
 *
 * Implements common methods & properties used by all Amazon services
 *
 * @extends Task
 * @version $ID$
 * @package phing.tasks.ext
 * @author Andrei Serdeliuc <andrei@serdeliuc.ro>
 * @abstract
 */
abstract class Service_Amazon extends Task
{
    /**
     * Collection of set options
     *
     * We set these magically so we can also load then from the environment
     *
     * (default value: array())
     *
     * @var array
     */
    protected $_options = array();

    /**
     * @param $var
     * @param $val
     */
    public function __set($var, $val)
    {
        $this->_options[$var] = $val;
    }

    /**
     * Property getter
     *
     * If the property hasn't been previously set (through the task call normally),
     * it will try to load it from the project
     *
     * This way, we can define global properties for the "Amazon" service, like key and secret
     *
     * @param  mixed $var
     * @return mixed
     */
    public function __get($var)
    {
        if (!isset($this->$var)) {
            if (!($val = $this->getProject()->getProperty('amazon.' . strtolower($var)))) {
                return false;
            } else {
                return $val;
            }
        }

        return $this->_options[$var];
    }

    /**
     * @param $var
     * @return bool
     */
    public function __isset($var)
    {
        return array_key_exists($var, $this->_options);
    }

    /**
     * @param $key
     * @throws BuildException
     */
    public function setKey($key)
    {
        if (empty($key) || !is_string($key)) {
            throw new BuildException('Key must be a non empty string');
        }

        $this->key = $key;
    }

    public function getKey()
    {
        if (!($key = $this->key)) {
            throw new BuildException('Key is not set');
        }

        return $key;
    }

    /**
     * @param $secret
     * @throws BuildException
     */
    public function setSecret($secret)
    {
        if (empty($secret) || !is_string($secret)) {
            throw new BuildException('Secret must be a non empty string');
        }

        $this->secret = $secret;
    }

    public function getSecret()
    {
        if (!($secret = $this->secret)) {
            throw new BuildException('Secret is not set');
        }

        return $this->secret;
    }
}
<?php
/**
 * $Id: f3db5ef14b0d98fcfbcabf12e421edb3dabc9508 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/simpletest/SimpleTestResultFormatter.php';

/**
 * Dummy result formatter used to count SimpleTest results
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: f3db5ef14b0d98fcfbcabf12e421edb3dabc9508 $
 * @package phing.tasks.ext.simpletest
 * @since 2.2.0
 */
class SimpleTestCountResultFormatter extends SimpleTestResultFormatter
{
    const SUCCESS = 0;
    const FAILURES = 1;
    const ERRORS = 2;

    /**
     * @return int
     */
    public function getRetCode()
    {
        if ($this->getExceptionCount() != 0) {
            return self::ERRORS;
        } else {
            if ($this->getFailCount() != 0) {
                return self::FAILURES;
            }
        }

        return self::SUCCESS;
    }
}
<?php
/**
 * $Id: 6a0564d9f5a6757229f329a0d39018796ae9c751 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/simpletest/SimpleTestResultFormatter.php';

/**
 * Prints plain text output of the test to a specified Writer.
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 6a0564d9f5a6757229f329a0d39018796ae9c751 $
 * @package phing.tasks.ext.simpletest
 * @since 2.2.0
 */
class SimpleTestDebugResultFormatter extends SimpleTestResultFormatter
{
    protected $current_case = "";
    protected $current_test = "";
    private $failingTests = array();

    public function printFailingTests()
    {
        foreach ($this->failingTests as $test) {
            $this->out->write($test . "\n");
        }
    }

    /**
     * @param string $test_name
     */
    public function paintCaseStart($test_name)
    {
        parent::paintCaseStart($test_name);
        $this->paint("Testsuite: $test_name\n");
        $this->current_case = $test_name;
    }

    /**
     * @param string $test_name
     */
    public function paintMethodStart($test_name)
    {
        parent::paintMethodStart($test_name);
        $this->current_test = $test_name;
        //$msg = "{$this->current_case} :: $test_name\n";
        $msg = "    TestCase: $test_name";
        $this->paint($msg);
    }

    /**
     * @param $msg
     */
    public function paint($msg)
    {
        if ($this->out == null) {
            print $msg;
        } else {
            $this->out->write($msg);
        }
    }

    /**
     * @param string $test_name
     */
    public function paintMethodEnd($test_name)
    {
        parent::paintMethodEnd($test_name);
        $this->paint("\n");
    }

    /**
     * @param string $test_name
     */
    public function paintCaseEnd($test_name)
    {
        parent::paintCaseEnd($test_name);
        $this->current_case = "";
        /* Only count suites where more than one test was run */

        if ($this->getRunCount() && false) {
            $sb = "";
            $sb .= "Tests run: " . $this->getRunCount();
            $sb .= ", Failures: " . $this->getFailureCount();
            $sb .= ", Errors: " . $this->getErrorCount();
            $sb .= ", Time elapsed: " . $this->getElapsedTime();
            $sb .= " sec\n";
            $this->paint($sb);
        }

    }

    /**
     * @param string $message
     */
    public function paintError($message)
    {
        parent::paintError($message);
        $this->formatError("ERROR", $message);
        $this->failingTests[] = $this->current_case . "->" . $this->current_test;
    }

    /**
     * @param string $message
     */
    public function paintFail($message)
    {
        parent::paintFail($message);
        $this->formatError("FAILED", $message);
        $this->failingTests[] = $this->current_case . "->" . $this->current_test;
    }

    /**
     * @param Exception $message
     */
    public function paintException($message)
    {
        parent::paintException($message);
        $this->failingTests[] = $this->current_case . "->" . $this->current_test;
        $this->formatError("Exception", $message);
    }

    /**
     * @param $type
     * @param $message
     */
    private function formatError($type, $message)
    {
        $this->paint("ERROR: $type: $message");
    }

}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/phpunit/FormatterElement.php';

/**
 * Child class of "FormatterElement", overrides setType to provide other
 * formatter classes for SimpleTest
 *
 * @author Michiel Rook <mrook@php.net>
 *
 * @package phing.tasks.ext.simpletest
 * @since 2.2.0
 */
class SimpleTestFormatterElement extends FormatterElement
{
    /**
     * @param string $type
     * @throws BuildException
     */
    public function setType($type)
    {
        $this->type = $type;

        if ($this->type == "xml") {
            require_once 'phing/tasks/ext/simpletest/SimpleTestXmlResultFormatter.php';
            $destFile = new PhingFile($this->toDir, 'testsuites.xml');
            $this->formatter = new SimpleTestXmlResultFormatter();
        } else {
            if ($this->type == "plain") {
                require_once 'phing/tasks/ext/simpletest/SimpleTestPlainResultFormatter.php';
                $this->formatter = new SimpleTestPlainResultFormatter();
            } else {
                if ($this->type == "summary") {
                    require_once 'phing/tasks/ext/simpletest/SimpleTestSummaryResultFormatter.php';
                    $this->formatter = new SimpleTestSummaryResultFormatter();
                } else {
                    if ($this->type == "debug") {
                        require_once 'phing/tasks/ext/simpletest/SimpleTestDebugResultFormatter.php';
                        $this->formatter = new SimpleTestDebugResultFormatter();
                    } else {
                        throw new BuildException("Formatter '" . $this->type . "' not implemented");
                    }
                }
            }
        }
    }
}
<?php
/**
 * $Id: 87be819ddfd82c1e76e794571d50890482e89394 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/simpletest/SimpleTestResultFormatter.php';

/**
 * Prints plain text output of the test to a specified Writer.
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 87be819ddfd82c1e76e794571d50890482e89394 $
 * @package phing.tasks.ext.simpletest
 * @since 2.2.0
 */
class SimpleTestPlainResultFormatter extends SimpleTestResultFormatter
{
    private $inner = "";

    /**
     * @return string
     */
    public function getExtension()
    {
        return ".txt";
    }

    /**
     * @return string
     */
    public function getPreferredOutfile()
    {
        return "testresults";
    }

    /**
     * @param string $test_name
     */
    public function paintCaseStart($test_name)
    {
        parent::paintCaseStart($test_name);

        $this->inner = "";
    }

    /**
     * @param string $test_name
     */
    public function paintCaseEnd($test_name)
    {
        parent::paintCaseEnd($test_name);

        $sb = "";
        /* Only count suites where more than one test was run */
        if ($this->getRunCount()) {
            $sb .= "Testsuite: $test_name\n";
            $sb .= "Tests run: " . $this->getRunCount();
            $sb .= ", Failures: " . $this->getFailureCount();
            $sb .= ", Errors: " . $this->getErrorCount();
            $sb .= ", Time elapsed: " . $this->getElapsedTime();
            $sb .= " sec\n";

            if ($this->out != null) {
                $this->out->write($sb);
                $this->out->write($this->inner);
            }
        }
    }

    /**
     * @param string $message
     */
    public function paintError($message)
    {
        parent::paintError($message);

        $this->formatError("ERROR", $message);
    }

    /**
     * @param string $message
     */
    public function paintFail($message)
    {
        parent::paintFail($message);

        $this->formatError("FAILED", $message);
    }

    /**
     * @param $type
     * @param $message
     */
    private function formatError($type, $message)
    {
        $this->inner .= $this->getTestName() . " " . $type . "\n";
        $this->inner .= $message . "\n";
    }
}
<?php
/**
 * $Id: 019c9ca9df741e3a91efdb19aa9988db486e725b $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

@include_once 'simpletest/scorer.php';

require_once 'phing/system/io/Writer.php';

/**
 * This abstract class describes classes that format the results of a SimpleTest testrun.
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 019c9ca9df741e3a91efdb19aa9988db486e725b $
 * @package phing.tasks.ext.simpletest
 * @since 2.2.0
 */
abstract class SimpleTestResultFormatter extends SimpleReporter
{
    protected $out = null;

    protected $project = null;

    private $timer = null;

    private $runCount = 0;

    private $failureCount = 0;

    private $errorCount = 0;

    private $currentTest = "";

    /**
     * Sets the writer the formatter is supposed to write its results to.
     * @param Writer $out
     */
    public function setOutput(Writer $out)
    {
        $this->out = $out;
    }

    /**
     * Returns the extension used for this formatter
     *
     * @return string the extension
     */
    public function getExtension()
    {
        return "";
    }

    /**
     * Sets the project
     *
     * @param Project the project
     */
    public function setProject(Project $project)
    {
        $this->project = $project;
    }

    /**
     * @return string
     */
    public function getPreferredOutfile()
    {
        return "";
    }

    /**
     * @param string $test_name
     */
    public function paintMethodStart($test_name)
    {
        parent::paintMethodStart($test_name);

        $this->currentTest = $test_name;
    }

    /**
     * @param string $test_name
     */
    public function paintMethodEnd($test_name)
    {
        parent::paintMethodEnd($test_name);

        $this->runCount++;
    }

    /**
     * @param string $test_name
     */
    public function paintCaseStart($test_name)
    {
        parent::paintCaseStart($test_name);

        $this->runCount = 0;
        $this->failureCount = 0;
        $this->errorCount = 0;

        $this->timer = new Timer();
        $this->timer->start();
    }

    /**
     * @param string $test_name
     */
    public function paintCaseEnd($test_name)
    {
        parent::paintCaseEnd($test_name);

        $this->timer->stop();
    }

    /**
     * @param string $message
     */
    public function paintError($message)
    {
        parent::paintError($message);

        $this->errorCount++;
    }

    /**
     * @param string $message
     */
    public function paintFail($message)
    {
        parent::paintFail($message);

        $this->failureCount++;
    }

    /**
     * @return int
     */
    public function getRunCount()
    {
        return $this->runCount;
    }

    /**
     * @return int
     */
    public function getFailureCount()
    {
        return $this->failureCount;
    }

    /**
     * @return int
     */
    public function getErrorCount()
    {
        return $this->errorCount;
    }

    /**
     * @return string
     */
    public function getTestName()
    {
        return $this->currentTest;
    }

    /**
     * @return int
     */
    public function getElapsedTime()
    {
        if ($this->timer) {
            return $this->timer->getElapsedTime();
        } else {
            return 0;
        }
    }
}
<?php
/**
 * $Id: 3b2f67ab5e1a8dec51d3e7f1128bcaeceee3e75c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/simpletest/SimpleTestResultFormatter.php';

/**
 * Prints short summary output of the test to Phing's logging system.
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 3b2f67ab5e1a8dec51d3e7f1128bcaeceee3e75c $
 * @package phing.tasks.ext.simpletest
 * @since 2.2.0
 */
class SimpleTestSummaryResultFormatter extends SimpleTestResultFormatter
{
    /**
     * @param string $test_name
     */
    public function paintCaseEnd($test_name)
    {
        parent::paintCaseEnd($test_name);

        /* Only count suites where more than one test was run */
        if ($this->getRunCount()) {
            $sb .= "Tests run: " . $this->getRunCount();
            $sb .= ", Failures: " . $this->getFailureCount();
            $sb .= ", Errors: " . $this->getErrorCount();
            $sb .= ", Time elapsed: " . $this->getElapsedTime();
            $sb .= " sec\n";

            if ($this->out != null) {
                $this->out->write($sb);
            }
        }
    }
}
<?php
/**
 * $Id: d94834e70b1522ae68a116f449d10cf309c2b063 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/system/io/PhingFile.php';
require_once 'phing/system/io/Writer.php';
require_once 'phing/util/LogWriter.php';

/**
 * Runs SimpleTest tests.
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: d94834e70b1522ae68a116f449d10cf309c2b063 $
 * @package phing.tasks.ext.simpletest
 * @since 2.2.0
 */
class SimpleTestTask extends Task
{
    private $formatters = array();
    private $haltonerror = false;
    private $haltonfailure = false;
    private $failureproperty;
    private $errorproperty;
    private $printsummary = false;
    private $testfailed = false;
    private $debug = false;

    /**
     * Initialize Task.
     * This method includes any necessary SimpleTest libraries and triggers
     * appropriate error if they cannot be found.  This is not done in header
     * because we may want this class to be loaded w/o triggering an error.
     */
    public function init()
    {
        @include_once 'simpletest/scorer.php';

        if (!class_exists('SimpleReporter')) {
            throw new BuildException("SimpleTestTask depends on SimpleTest package being installed.", $this->getLocation(
            ));
        }

        require_once 'simpletest/reporter.php';
        require_once 'simpletest/xml.php';
        require_once 'simpletest/test_case.php';
        require_once 'phing/tasks/ext/simpletest/SimpleTestCountResultFormatter.php';
        require_once 'phing/tasks/ext/simpletest/SimpleTestDebugResultFormatter.php';
        require_once 'phing/tasks/ext/simpletest/SimpleTestFormatterElement.php';
    }

    /**
     * @param $value
     */
    public function setFailureproperty($value)
    {
        $this->failureproperty = $value;
    }

    /**
     * @param $value
     */
    public function setErrorproperty($value)
    {
        $this->errorproperty = $value;
    }

    /**
     * @param $value
     */
    public function setHaltonerror($value)
    {
        $this->haltonerror = $value;
    }

    /**
     * @param $value
     */
    public function setHaltonfailure($value)
    {
        $this->haltonfailure = $value;
    }

    /**
     * @param $printsummary
     */
    public function setPrintsummary($printsummary)
    {
        $this->printsummary = $printsummary;
    }

    /**
     * @param $debug
     */
    public function setDebug($debug)
    {
        $this->debug = $debug;
    }

    /**
     * @return bool
     */
    public function getDebug()
    {
        return $this->debug;
    }

    /**
     * Add a new formatter to all tests of this task.
     *
     * @param SimpleTestFormatterElement formatter element
     */
    public function addFormatter(SimpleTestFormatterElement $fe)
    {
        $this->formatters[] = $fe;
    }

    /**
     * Add a new fileset containing the XML results to aggregate
     *
     * @param FileSet the new fileset containing XML results.
     */
    public function addFileSet(FileSet $fileset)
    {
        $this->filesets[] = $fileset;
    }

    /**
     * Iterate over all filesets and return the filename of all files
     * that end with .php.
     *
     * @return array an array of filenames
     */
    private function getFilenames()
    {
        $filenames = array();

        foreach ($this->filesets as $fileset) {
            $ds = $fileset->getDirectoryScanner($this->project);
            $ds->scan();

            $files = $ds->getIncludedFiles();

            foreach ($files as $file) {
                if (strstr($file, ".php")) {
                    $filenames[] = $ds->getBaseDir() . "/" . $file;
                }
            }
        }

        return $filenames;
    }

    /**
     * The main entry point
     *
     * @throws BuildException
     */
    public function main()
    {
        $suite = new TestSuite();

        $filenames = $this->getFilenames();

        foreach ($filenames as $testfile) {
            $suite->addFile($testfile);
        }

        if ($this->debug) {
            $fe = new SimpleTestFormatterElement();
            $fe->setType('debug');
            $fe->setUseFile(false);
            $this->formatters[] = $fe;
        }

        if ($this->printsummary) {
            $fe = new SimpleTestFormatterElement();
            $fe->setType('summary');
            $fe->setUseFile(false);
            $this->formatters[] = $fe;
        }

        foreach ($this->formatters as $fe) {
            $formatter = $fe->getFormatter();
            $formatter->setProject($this->getProject());

            if ($fe->getUseFile()) {
                $destFile = new PhingFile($fe->getToDir(), $fe->getOutfile());

                $writer = new FileWriter($destFile->getAbsolutePath());

                $formatter->setOutput($writer);
            } else {
                $formatter->setOutput($this->getDefaultOutput());
            }
        }

        $this->execute($suite);

        if ($this->testfailed && $this->formatters[0]->getFormatter() instanceof SimpleTestDebugResultFormatter) {
            $this->getDefaultOutput()->write("Failed tests: ");
            $this->formatters[0]->getFormatter()->printFailingTests();
        }

        if ($this->testfailed) {
            throw new BuildException("One or more tests failed");
        }
    }

    /**
     * @param $suite
     */
    private function execute($suite)
    {
        $counter = new SimpleTestCountResultFormatter();
        $reporter = new MultipleReporter();
        $reporter->attachReporter($counter);

        foreach ($this->formatters as $fe) {
            // SimpleTest 1.0.1 workaround
            $formatterList[] = $fe->getFormatter();

            $reporter->attachReporter(end($formatterList));
        }

        $suite->run($reporter);

        $retcode = $counter->getRetCode();

        if ($retcode == SimpleTestCountResultFormatter::ERRORS) {
            if ($this->errorproperty) {
                $this->project->setNewProperty($this->errorproperty, true);
            }

            if ($this->haltonerror) {
                $this->testfailed = true;
            }
        } elseif ($retcode == SimpleTestCountResultFormatter::FAILURES) {
            if ($this->failureproperty) {
                $this->project->setNewProperty($this->failureproperty, true);
            }

            if ($this->haltonfailure) {
                $this->testfailed = true;
            }
        }
    }

    /**
     * @return LogWriter
     */
    private function getDefaultOutput()
    {
        return new LogWriter($this);
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/simpletest/SimpleTestResultFormatter.php';
@include_once 'simpletest/xml.php';

/**
 * Prints plain text output of the test to a specified Writer.
 *
 * @author Michiel Rook <mrook@php.net>
 *
 * @package phing.tasks.ext.simpletest
 * @since 2.2.0
 */
class SimpleTestXmlResultFormatter extends SimpleTestResultFormatter
{
    /**
     * @var XmlReporter
     */
    private $logger = null;

    private $xmlData = "";

    /**
     *
     */
    public function __construct()
    {
        $this->logger = new XmlReporter();
    }

    /**
     * @return string
     */
    public function getExtension()
    {
        return ".xml";
    }

    /**
     * @return string
     */
    public function getPreferredOutfile()
    {
        return "testsuites";
    }

    private function captureStart()
    {
        ob_start();
    }

    private function captureStop()
    {
        $this->xmlData .= ob_get_contents();
        ob_end_clean();
    }

    /**
     * @param string $test_name
     * @param int $size
     */
    public function paintGroupStart($test_name, $size)
    {
        parent::paintGroupStart($test_name, $size);

        $this->captureStart();
        $this->logger->paintGroupStart($test_name, $size);
        $this->captureStop();
    }

    /**
     * @param string $test_name
     */
    public function paintGroupEnd($test_name)
    {
        parent::paintGroupEnd($test_name);

        $this->captureStart();
        $this->logger->paintGroupEnd($test_name);
        $this->captureStop();

        if (count($this->_test_stack) == 0) {
            if ($this->out) {
                $this->out->write($this->xmlData);
                $this->out->close();
            }
        }
    }

    /**
     * @param string $test_name
     */
    public function paintCaseStart($test_name)
    {
        $this->captureStart();
        $this->logger->paintCaseStart($test_name);
        $this->captureStop();
    }

    /**
     * @param string $test_name
     */
    public function paintCaseEnd($test_name)
    {
        $this->captureStart();
        $this->logger->paintCaseEnd($test_name);
        $this->captureStop();
    }

    /**
     * @param string $test_name
     */
    public function paintMethodStart($test_name)
    {
        $this->captureStart();
        $this->logger->paintMethodStart($test_name);
        $this->captureStop();
    }

    /**
     * @param string $test_name
     */
    public function paintMethodEnd($test_name)
    {
        $this->captureStart();
        $this->logger->paintMethodEnd($test_name);
        $this->captureStop();
    }

    /**
     * @param string $message
     */
    public function paintPass($message)
    {
        $this->captureStart();
        $this->logger->paintPass($message);
        $this->captureStop();
    }

    /**
     * @param string $message
     */
    public function paintError($message)
    {
        $this->captureStart();
        $this->logger->paintError($message);
        $this->captureStop();
    }

    /**
     * @param string $message
     */
    public function paintFail($message)
    {
        $this->captureStart();
        $this->logger->paintFail($message);
        $this->captureStop();
    }

    /**
     * @param Exception $exception
     */
    public function paintException($exception)
    {
        $this->captureStart();
        $this->logger->paintException($exception);
        $this->captureStop();
    }

    /**
     * @param string $message
     */
    public function paintSkip($message)
    {
        $this->captureStart();
        $this->logger->paintSkip($message);
        $this->captureStop();
    }

    /**
     * @param string $message
     */
    public function paintMessage($message)
    {
        $this->captureStart();
        $this->logger->paintMessage($message);
        $this->captureStop();
    }

    /**
     * @param string $message
     */
    public function paintFormattedMessage($message)
    {
        $this->captureStart();
        $this->logger->paintFormattedMessage($message);
        $this->captureStop();
    }

    /**
     * @param string $type
     * @param mixed $payload
     */
    public function paintSignal($type, $payload)
    {
        $this->captureStart();
        $this->logger->paintSignal($type, $payload);
        $this->captureStop();
    }
}
<?php

/*
 *  $Id: 7c13e08dfd78388be8bdd0920677dc855c78c525 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/BuildException.php';
include_once 'phing/util/StringHelper.php';

/**
 * A phing task for generating output by using Smarty.
 *
 * This is based on the TexenTask from Apache's Velocity engine.  This class
 * was originally proted in order to provide a template compiling system for
 * Torque.
 *
 * TODO:
 *        - Add Path / useClasspath support?
 *
 * @author    Hans Lellelid <hans@xmpl.org> (SmartyTask)
 * @author    Jason van Zyl <jvanzyl@apache.org> (TexenTask)
 * @author    Robert Burrell Donkin <robertdonkin@mac.com>
 * @version   $Id: 7c13e08dfd78388be8bdd0920677dc855c78c525 $
 * @package   phing.tasks.ext
 */
class SmartyTask extends Task
{

    /**
     * Smarty template engine.
     * @var Smarty
     */
    protected $context;

    /**
     * Variables that are assigned to the context on parse/compile.
     * @var array
     */
    protected $properties = array();

    /**
     * This is the control template that governs the output.
     * It may or may not invoke the services of worker
     * templates.
     * @var string
     */
    protected $controlTemplate;

    /**
     * This is where Smarty will look for templates
     * using the file template loader.
     * @var string
     */
    protected $templatePath;

    /**
     * This is where texen will place all the output
     * that is a product of the generation process.
     * @var string
     */
    protected $outputDirectory;

    /**
     * This is the file where the generated text
     * will be placed.
     * @var string
     */
    protected $outputFile;

    /**
     * <p>
     * These are properties that are fed into the
     * initial context from a properties file. This
     * is simply a convenient way to set some values
     * that you wish to make available in the context.
     * </p>
     * <p>
     * These values are not critical, like the template path
     * or output path, but allow a convenient way to
     * set a value that may be specific to a particular
     * generation task.
     * </p>
     * <p>
     * For example, if you are generating scripts to allow
     * user to automatically create a database, then
     * you might want the <code>$databaseName</code>
     * to be placed
     * in the initial context so that it is available
     * in a script that might look something like the
     * following:
     * <code><pre>
     * #!bin/sh
     *
     * echo y | mysqladmin create $databaseName
     * </pre></code>
     * The value of <code>$databaseName</code> isn't critical to
     * output, and you obviously don't want to change
     * the ant task to simply take a database name.
     * So initial context values can be set with
     * properties file.
     *
     * @var array
     */
    protected $contextProperties;

    /**
     * Smarty compiles templates before parsing / replacing tokens in them.
     * By default it will try ./templates_c, but you may wish to override this.
     * @var string
     */
    protected $compilePath;

    /**
     * Whether to force Smarty to recompile templates.
     * Smarty does check file modification time, but you can set this
     * to be *sure* that the template will be compiled (of course it will
     * be slower if you do).
     * @var boolean
     */
    protected $forceCompile = false;

    /**
     * Smarty can use config files.
     * This tells Smarty where to look for the config files.
     * @var string
     */
    protected $configPath;

    /**
     * Customize the left delimiter for Smarty tags.
     * @var string
     */
    protected $leftDelimiter;

    /**
     * Customize the right delimiter for Smarty tags.
     * @var string
     */
    protected $rightDelimiter;

    // -----------------------------------------------------------------------
    // The following getters & setters are used by phing to set properties
    // specified in the XML for the smarty task.
    // -----------------------------------------------------------------------

    public function init()
    {
        // This check returns true for smarty 3 and false otherwise.
        if (stream_resolve_include_path('SmartyBC.class.php')) {
            include_once 'SmartyBC.class.php';
        } else {
            include_once 'Smarty.class.php';
        }

        if (!class_exists('Smarty')) {
            throw new BuildException("To use SmartyTask, you must have the path to Smarty.class.php on your include_path or your \$PHP_CLASSPATH environment variable.");
        }
    }

    /**
     * [REQUIRED] Set the control template for the
     * generating process.
     * @param  string $controlTemplate
     * @return void
     */
    public function setControlTemplate($controlTemplate)
    {
        $this->controlTemplate = $controlTemplate;
    }

    /**
     * Get the control template for the
     * generating process.
     * @return string
     */
    public function getControlTemplate()
    {
        return $this->controlTemplate;
    }

    /**
     * [REQUIRED] Set the path where Smarty will look
     * for templates using the file template
     * loader.
     * @param $templatePath
     * @return void
     */
    public function setTemplatePath($templatePath)
    {
        $resolvedPath = "";
        $tok = strtok($templatePath, ",");
        while ($tok) {
            // resolve relative path from basedir and leave
            // absolute path untouched.
            $fullPath = $this->project->resolveFile($tok);
            $cpath = $fullPath->getCanonicalPath();
            if ($cpath === false) {
                $this->log("Template directory does not exist: " . $fullPath->getAbsolutePath());
            } else {
                $resolvedPath .= $cpath;
            }
            $tok = strtok(",");
            if ($tok) {
                $resolvedPath .= ",";
            }
        }
        $this->templatePath = $resolvedPath;
    }

    /**
     * Get the path where Smarty will look
     * for templates using the file template
     * loader.
     * @return string
     */
    public function getTemplatePath()
    {
        return $this->templatePath;
    }

    /**
     * [REQUIRED] Set the output directory. It will be
     * created if it doesn't exist.
     * @param  PhingFile $outputDirectory
     * @return void
     * @throws Exception
     */
    public function setOutputDirectory(PhingFile $outputDirectory)
    {
        try {
            if (!$outputDirectory->exists()) {
                $this->log(
                    "Output directory does not exist, creating: " . $outputDirectory->getPath(),
                    Project::MSG_VERBOSE
                );
                if (!$outputDirectory->mkdirs()) {
                    throw new IOException("Unable to create Ouptut directory: " . $outputDirectory->getAbsolutePath());
                }
            }
            $this->outputDirectory = $outputDirectory->getCanonicalPath();
        } catch (IOException $ioe) {
            throw new BuildException($ioe->getMessage());
        }
    }

    /**
     * Get the output directory.
     * @return string
     */
    public function getOutputDirectory()
    {
        return $this->outputDirectory;
    }

    /**
     * [REQUIRED] Set the output file for the
     * generation process.
     * @param $outputFile
     * @return void
     */
    public function setOutputFile($outputFile)
    {
        $this->outputFile = $outputFile;
    }

    /**
     * Get the output file for the
     * generation process.
     * @return string
     */
    public function getOutputFile()
    {
        return $this->outputFile;
    }

    /**
     * Set the path Smarty uses as a "cache" for compiled templates.
     * @param string $compilePath
     */
    public function setCompilePath($compilePath)
    {
        $this->compilePath = $compilePath;
    }

    /**
     * Get the path Smarty uses for compiling templates.
     * @return string
     */
    public function getCompilePath()
    {
        return $this->compilePath;
    }

    /**
     * Set whether Smarty should always recompile templates.
     * @param  boolean $force
     * @return void
     */
    public function setForceCompile($force)
    {
        $this->forceCompile = (boolean) $force;
    }

    /**
     * Get whether Smarty should always recompile template.
     * @return boolean
     */
    public function getForceCompile()
    {
        return $this->forceCompile;
    }

    /**
     * Set where Smarty looks for config files.
     * @param  string $configPath
     * @return void
     */
    public function setConfigPath($configPath)
    {
        $this->configPath = $configPath;
    }

    /**
     * Get the path that Smarty uses for looking for config files.
     * @return string
     */
    public function getConfigPath()
    {
        return $this->configPath;
    }

    /**
     * Set Smarty template left delimiter.
     * @param  string $delim
     * @return void
     */
    public function setLeftDelimiter($delim)
    {
        $this->leftDelimiter = $delim;
    }

    /**
     * Get Smarty template right delimiter
     * @return string
     */
    public function getLeftDelimiter()
    {
        return $this->leftDelimiter;
    }

    /**
     * Set Smarty template right delimiter.
     * @param  string $delim
     * @return void
     */
    public function setRightDelimiter($delim)
    {
        $this->rightDelimiter = $delim;
    }

    /**
     * Get Smarty template right delimiter
     * @return string
     */
    public function getRightDelimiter()
    {
        return $this->rightDelimiter;
    }

    /**
     * Set the context properties that will be
     * fed into the initial context be the
     * generating process starts.
     * @param  string $file
     * @throws BuildException
     * @return void
     */
    public function setContextProperties($file)
    {

        $sources = explode(",", $file);
        $this->contextProperties = new Properties();

        // Always try to get the context properties resource
        // from a file first. Templates may be taken from a JAR
        // file but the context properties resource may be a
        // resource in the filesystem. If this fails than attempt
        // to get the context properties resource from the
        // classpath.
        for ($i = 0, $sourcesLength = count($sources); $i < $sourcesLength; $i++) {
            $source = new Properties();

            try {

                // resolve relative path from basedir and leave
                // absolute path untouched.
                $fullPath = $this->project->resolveFile($sources[$i]);
                $this->log("Using contextProperties file: " . $fullPath->__toString());
                $source->load($fullPath);

            } catch (Exception $e) {

                throw new BuildException("Context properties file " . $sources[$i] .
                    " could not be found in the file system!");

            }

            $keys = $source->keys();

            foreach ($keys as $key) {
                $name = $key;
                $value = $this->project->replaceProperties($source->getProperty($name));
                $this->contextProperties->setProperty($name, $value);
            }
        }
    }

    /**
     * Get the context properties that will be
     * fed into the initial context be the
     * generating process starts.
     * @return Properties
     */
    public function getContextProperties()
    {
        return $this->contextProperties;
    }

    // ---------------------------------------------------------------
    // End of XML setters & getters
    // ---------------------------------------------------------------

    /**
     * Creates a Smarty object.
     *
     * @return Smarty    initialized (cleared) Smarty context.
     * @throws Exception the execute method will catch
     *                   and rethrow as a <code>BuildException</code>
     */
    public function initControlContext()
    {
        $this->context->clear_all_assign();

        return $this->context;
    }

    /**
     * Execute the input script with Smarty
     *
     * @throws BuildException
     *                        BuildExceptions are thrown when required attributes are missing.
     *                        Exceptions thrown by Smarty are rethrown as BuildExceptions.
     */
    public function main()
    {

        // Make sure the template path is set.
        if (empty($this->templatePath)) {
            throw new BuildException("The template path needs to be defined!");
        }

        // Make sure the control template is set.
        if ($this->controlTemplate === null) {
            throw new BuildException("The control template needs to be defined!");
        }

        // Make sure the output directory is set.
        if ($this->outputDirectory === null) {
            throw new BuildException("The output directory needs to be defined!");
        }

        // Make sure there is an output file.
        if ($this->outputFile === null) {
            throw new BuildException("The output file needs to be defined!");
        }

        // Setup Smarty runtime.

        // Smarty uses one object to store properties and to store
        // the context for the template (unlike Smarty).  We setup this object, calling it
        // $this->context, and then initControlContext simply zeros out
        // any assigned variables.
        //
        // Use the smarty backwards compatibility layer if existent.
        if (class_exists('SmartyBC')) {
            $this->context = new SmartyBC();
        } else {
            $this->context = new Smarty();
        }

        if ($this->compilePath !== null) {
            $this->log("Using compilePath: " . $this->compilePath);
            $this->context->compile_dir = $this->compilePath;
        }

        if ($this->configPath !== null) {
            $this->log("Using configPath: " . $this->configPath);
            $this->context->config_dir = $this->configPath;
        }

        if ($this->forceCompile !== null) {
            $this->context->force_compile = $this->forceCompile;
        }

        if ($this->leftDelimiter !== null) {
            $this->context->left_delimiter = $this->leftDelimiter;
        }

        if ($this->rightDelimiter !== null) {
            $this->context->right_delimiter = $this->rightDelimiter;
        }

        if ($this->templatePath !== null) {
            $this->log("Using templatePath: " . $this->templatePath);
            $this->context->template_dir = $this->templatePath;
        }

        $smartyCompilePath = new PhingFile($this->context->compile_dir);
        if (!$smartyCompilePath->exists()) {
            $this->log(
                "Compile directory does not exist, creating: " . $smartyCompilePath->getPath(),
                Project::MSG_VERBOSE
            );
            if (!$smartyCompilePath->mkdirs()) {
                throw new BuildException("Smarty needs a place to compile templates; specify a 'compilePath' or create " . $this->context->compile_dir);
            }
        }

        // Make sure the output directory exists, if it doesn't
        // then create it.
        $file = new PhingFile($this->outputDirectory);
        if (!$file->exists()) {
            $this->log("Output directory does not exist, creating: " . $file->getAbsolutePath());
            $file->mkdirs();
        }

        $path = $this->outputDirectory . DIRECTORY_SEPARATOR . $this->outputFile;
        $this->log("Generating to file " . $path);

        $writer = new FileWriter($path);

        // The generator and the output path should
        // be placed in the init context here and
        // not in the generator class itself.
        $c = $this->initControlContext();

        // Set any variables that need to always
        // be loaded
        $this->populateInitialContext($c);

        // Feed all the options into the initial
        // control context so they are available
        // in the control/worker templates.
        if ($this->contextProperties !== null) {

            foreach ($this->contextProperties->keys() as $property) {

                $value = $this->contextProperties->getProperty($property);

                // Special exception (from Texen)
                // for properties ending in file.contents:
                // in that case we dump the contents of the file
                // as the "value" for the Property.
                if (StringHelper::endsWith("file.contents", $property)) {
                    // pull in contents of file specified

                    $property = substr($property, 0, strpos($property, "file.contents") - 1);

                    // reset value, and then
                    // read in the contents of the file into that var
                    $value = "";
                    $f = new PhingFile($this->project->resolveFile($value)->getCanonicalPath());
                    if ($f->exists()) {
                        try {
                            $fr = new FileReader($f);
                            $fr->readInto($value);
                        } catch (Exception $e) {
                            throw $e;
                        }
                    }

                } // if ends with file.contents

                if (StringHelper::isBoolean($value)) {
                    $value = StringHelper::booleanValue($value);
                }

                $c->assign($property, $value);

            } // foreach property

        } // if contextProperties !== null

        try {
            //$c->display($this->controlTemplate);
            $writer->write($c->fetch($this->controlTemplate));
            $writer->close();
        } catch (IOException $ioe) {
            $writer->close();
            throw new BuildException("Cannot write parsed template.");
        }

        $this->cleanup();
    }

    /**
     * <p>Place useful objects into the initial context.</p>
     *
     * <p>TexenTask places <code>Date().toString()</code> into the
     * context as <code>$now</code>.  Subclasses who want to vary the
     * objects in the context should override this method.</p>
     *
     * <p><code>$generator</code> is not put into the context in this
     * method.</p>
     *
     * @param Smarty|The $context
     * @internal param The $context context to populate, as retrieved from
     * {@link #initControlContext()}.
     * @return void
     */
    protected function populateInitialContext(Smarty $context)
    {
    }

    /**
     * A hook method called at the end of {@link #execute()} which can
     * be overridden to perform any necessary cleanup activities (such
     * as the release of database connections, etc.).  By default,
     * does nothing.
     * @return void
     * @throws Exception Problem cleaning up.
     */
    protected function cleanup()
    {
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/BuildException.php';
require_once 'phing/Project.php';
require_once 'phing/tasks/ext/sonar/SonarProperty.php';

/**
 *
 * @author Bernhard Mendl <mail@bernhard-mendl.de>
 * @package phing.tasks.ext.sonar
 */
class SonarConfigurationFileParser
{

    /**
     *
     * @var Project
     */
    private $project;

    /**
     *
     * @var string
     */
    private $file;

    /**
     * This map holds the properties read from the configuration file.
     *
     * @var array
     */
    private $properties;

    /**
     * Name of currently parsed property.
     *
     * @var string|null
     */
    private $name;

    /**
     * Value of currently parsed property.
     *
     * @var string
     */
    private $value;

    /**
     *
     * @param string $file
     *            The properties file.
     */
    public function __construct($file, Project $project)
    {
        if (($file === null) || ($file === '')) {
            throw new BuildException('File name must not be null or empty.');
        }

        $this->file = $file;
        $this->project = $project;
    }

    /**
     *
     * @throws BuildException
     * @return array
     */
    public function parse()
    {
        $this->properties = array();

        $contents = @file_get_contents($this->file);

        if ($contents === false) {
            $message = sprintf('Could not read file [%s].', $this->file);
            throw new BuildException($message);
        }

        $lines = explode("\n", $contents);
        $count = count($lines);
        $isMultiLine = false;
        for ($i = 0; $i < $count; $i ++) {
            $line = $lines[$i];

            if ($isMultiLine) {
                $isMultiLine = $this->extractContinuedValue($line);
            } else {
                $this->name = null;
                $this->value = '';

                $isMultiLine = $this->extractNameAndValue($line);
            }

            if (($this->name !== null) && (! $isMultiLine)) {
                if (array_key_exists($this->name, $this->properties)) {
                    $message = sprintf('Property [%s] overwritten: old value [%s], new value [%s].',
                        $this->name,
                        $this->properties[$this->name],
                        $this->value);
                    $this->project->log($message, Project::MSG_WARN);
                }

                // Unescape backslashes.
                $this->value = str_replace('\\\\', '\\', $this->value);

                $this->properties[$this->name] = $this->value;
            }
        }

        if ($isMultiLine) {
            $message = sprintf('Last property looks like a multi-lined value, but end of file found. Name = [%s].', $this->name);
            throw new BuildException($message);
        }

        return $this->properties;
    }

    /**
     *
     * @param string $line
     * @return boolean
     */
    private function extractNameAndValue($line)
    {
        $isMultiLine = false;

        if ($this->isCommentLine($line)) {
            return $isMultiLine;
        }

        // Find key and value.
        $hasMatch = preg_match('/\\s*([^=:]*[^=:\\s]+)\\s*[=:]\\s*(.*)$/s', $line, $matches);

        if (($hasMatch === 1) && (count($matches) === 3)) {
            $this->name = $matches[1];
            $this->value = $matches[2];

            $isMultiLine = $this->checkMultiLine();
        }

        return $isMultiLine;
    }

    /**
     *
     * @param string $line
     * @return boolean
     */
    private function extractContinuedValue($line) {
        $isMultiLine = false;

        if ($this->isCommentLine($line)) {
            return $isMultiLine;
        }

        // Find continued value.
        $hasMatch = preg_match('/\\s*(.*)$/s', $line, $matches);

        if (($hasMatch === 1) && (count($matches) === 2)) {
            $this->value .= $matches[1];

            $isMultiLine = $this->checkMultiLine();
        }

        return $isMultiLine;
    }

    /**
     *
     * @return boolean
     */
    private function checkMultiLine()
    {
        $isMultiLine = false;

        // Is there a single(!) backslash at the end of the line?
        if (preg_match('/[^\\\]\\\$/', $this->value) === 1) {
            // Remove last char, i.e. the backslash.
            $this->value = substr($this->value, 0, -1);
            $isMultiLine = true;
        }

        return $isMultiLine;
    }

    /**
     *
     * @param string $line
     * @return boolean
     */
    private function isCommentLine($line)
    {
        return preg_match('/^\\s*[!#]/', $line) === 1;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * A property element nested in SonarTask.
 *
 * @author Bernhard Mendl <mail@bernhard-mendl.de>
  * @package phing.tasks.ext.sonar
 */
class SonarProperty
{

    /**
     *
     * @var string
     */
    private $name;

    /**
     *
     * @var string
     */
    private $value;

    /**
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     *
     * @param string $name
     * @return void
     */
    public function setName($name)
    {
        $this->name = (string) $name;
    }

    /**
     *
     * @return string
     */
    public function getValue()
    {
        return $this->value;
    }

    /**
     *
     * @param string $value
     * @return void
     */
    public function setValue($value)
    {
        $this->value = (string) $value;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/BuildException.php';
require_once 'phing/Project.php';
require_once 'phing/Task.php';
require_once 'phing/tasks/ext/sonar/SonarConfigurationFileParser.php';
require_once 'phing/tasks/ext/sonar/SonarProperty.php';

/**
 * Runs SonarQube Scanner.
 *
 * @author Bernhard Mendl <mail@bernhard-mendl.de>
 * @package phing.tasks.ext.sonar
 * @see http://www.sonarqube.org
 */
class SonarTask extends Task
{

    const EXIT_SUCCESS = 0;

    /**
     *
     * @var string|null
     */
    private $executable = null;

    /**
     *
     * @var string
     */
    private $errors = 'false';

    /**
     *
     * @var string
     */
    private $debug = 'false';

    /**
     *
     * @var string|null
     */
    private $configuration = null;

    /**
     *
     * @var array Nested *Property* elements.
     * @see Property
     */
    private $propertyElements = array();

    /**
     * The command-line options passed to the SonarQube Scanner executable.
     *
     * @var array
     */
    private $commandLineOptions = array();

    /**
     * Map containing SonarQube's "analysis parameters".
     *
     * Map keys are SonarQube parameter names. Map values are parameter values.
     * See {@link http://docs.sonarqube.org/display/SONAR/Analysis+Parameters}.
     *
     * @var array
     */
    private $properties = array();

    /**
     * Sets the path of the SonarQube Scanner executable.
     *
     * If the SonarQube Scanner is included in the PATH environment variable,
     * the file name is sufficient.
     *
     * @param string $executable
     * @return void
     */
    public function setExecutable($executable)
    {
        $this->executable = (string) $executable;

        $message = sprintf("Set executable to [%s].", $this->executable);
        $this->log($message, Project::MSG_DEBUG);
    }

    /**
     * Sets or unsets the "--errors" flag of SonarQube Scanner.
     *
     * @param string $errors
     *            Allowed values are "true"/"false", "yes"/"no", or "1"/"0".
     * @return void
     */
    public function setErrors($errors)
    {
        $this->errors = strtolower((string) $errors);

        $message = sprintf("Set errors flag to [%s].", $this->errors);
        $this->log($message, Project::MSG_DEBUG);
    }

    /**
     * Sets or unsets the "--debug" flag of SonarQube Scanner.
     *
     * @param string $debug
     *            Allowed values are "true"/"false", "yes"/"no", or "1"/"0".
     * @return void
     */
    public function setDebug($debug)
    {
        $this->debug = strtolower((string) $debug);

        $message = sprintf("Set debug flag to [%s].", $this->debug);
        $this->log($message, Project::MSG_DEBUG);
    }

    /**
     * Sets the path of a configuration file for SonarQube Scanner.
     *
     * @param string $configuration
     * @return void
     */
    public function setConfiguration($configuration)
    {
        $this->configuration = (string) $configuration;

        $message = sprintf("Set configuration to [%s].", $this->configuration);
        $this->log($message, Project::MSG_DEBUG);
    }

    /**
     * Adds a nested Property element.
     *
     * @param SonarProperty $property
     * @return void
     */
    public function addProperty(SonarProperty $property)
    {
        $this->propertyElements[] = $property;

        $message = sprintf("Added property: [%s] = [%s].", $property->getName(), $property->getValue());
        $this->log($message, Project::MSG_DEBUG);
    }

    /**
     *
     * {@inheritdoc}
     *
     * @see Task::init()
     */
    public function init()
    {
        $this->checkExecAllowed();
    }

    /**
     *
     * {@inheritdoc}
     *
     * @see Task::main()
     */
    public function main()
    {
        $this->validateErrors();
        $this->validateDebug();
        $this->validateConfiguration();
        $this->validateProperties();
        $this->validateExecutable();

        $command = sprintf('%s %s', escapeshellcmd($this->executable), $this->constructOptionsString());

        $message = sprintf('Executing: [%s]', $command);
        $this->log($message, Project::MSG_VERBOSE);

        exec($command, $output, $returnCode);

        foreach ($output as $line) {
            $this->log($line);
        }

        if ($returnCode !== self::EXIT_SUCCESS) {
            throw new BuildException('Execution of SonarQube Scanner failed.');
        }
    }

    /**
     * Constructs command-line options string for SonarQube Scanner.
     *
     * @return string
     */
    private function constructOptionsString()
    {
        $options = implode(' ', $this->commandLineOptions);

        foreach ($this->properties as $name => $value) {
            $arg = sprintf('%s=%s', $name, $value);
            $options .= ' -D ' . escapeshellarg($arg);
        }

        return $options;
    }

    /**
     * Check whether PHP function 'exec()' is available.
     *
     * @throws BuildException
     * @return void
     */
    private function checkExecAllowed()
    {
        if (! function_exists('exec') || ! is_callable('exec')) {
            $message = 'Cannot execute SonarQube Scanner because calling PHP function exec() is not permitted by PHP configuration.';
            throw new BuildException($message);
        }
    }

    /**
     *
     * @throws BuildException
     * @return void
     */
    private function validateExecutable()
    {
        if (($this->executable === null) || ($this->executable === '')) {
            $message = 'You must specify the path of the SonarQube Scanner using the "executable" attribute.';
            throw new BuildException($message);
        }

        // Note that executable is used as argument here.
        $escapedExecutable = escapeshellarg($this->executable);

        if ($this->isWindows()) {
            $message = 'Assuming a Windows system. Looking for SonarQube Scanner ...';
            $command = 'where ' . $escapedExecutable;
        } else {
            $message = 'Assuming a Linux or Mac system. Looking for SonarQube Scanner ...';
            $command = 'which ' . $escapedExecutable;
        }

        $this->log($message, Project::MSG_VERBOSE);
        unset($output);
        exec($command, $output, $returnCode);

        if ($returnCode !== self::EXIT_SUCCESS) {
            $message = sprintf('Cannot find SonarQube Scanner: [%s].', $this->executable);
            throw new BuildException($message);
        }

        // Verify that executable is indeed SonarQube Scanner ...
        $escapedExecutable = escapeshellcmd($this->executable);
        unset($output);
        exec($escapedExecutable . ' --version', $output, $returnCode);

        if ($returnCode !== self::EXIT_SUCCESS) {
            $message = sprintf('Could not check version string. Executable appears not to be SonarQube Scanner: [%s].', $this->executable);
            throw new BuildException($message);
        }

        $isOk = false;
        foreach ($output as $line) {
            if (preg_match('/SonarQube Scanner [0-9]+\\.[0-9]+/', $line) === 1) {
                $isOk = true;
                break;
            }
        }

        if ($isOk) {
            $message = sprintf('Found SonarQube Scanner: [%s].', $this->executable);
            $this->log($message, Project::MSG_VERBOSE);
        } else {
            $message = sprintf('Could not find name of SonarQube Scanner in version string. Executable appears not to be SonarQube Scanner: [%s].', $this->executable);
            throw new BuildException($message);
        }
    }

    /**
     *
     * @throws BuildException
     * @return void
     */
    private function validateErrors()
    {
        if (($this->errors === '1') || ($this->errors === 'true') || ($this->errors === 'yes')) {
            $errors = true;
        } elseif (($this->errors === '0') || ($this->errors === 'false') || ($this->errors === 'no')) {
            $errors = false;
        } else {
            throw new BuildException('Expected a boolean value.');
        }

        if ($errors) {
            $this->commandLineOptions[] = '--errors';
        }
    }

    /**
     *
     * @throws BuildException
     * @return void
     */
    private function validateDebug()
    {
        if (($this->debug === '1') || ($this->debug === 'true') || ($this->debug === 'yes')) {
            $debug = true;
        } elseif (($this->debug === '0') || ($this->debug === 'false') || ($this->debug === 'no')) {
            $debug = false;
        } else {
            throw new BuildException('Expected a boolean value.');
        }

        if ($debug) {
            $this->commandLineOptions[] = '--debug';
        }
    }

    /**
     *
     * @throws BuildException
     * @return void
     */
    private function validateConfiguration()
    {
        if (($this->configuration === null) || ($this->configuration === '')) {
            // NOTE: Ignore an empty configuration. This allows for
            // using Phing properties as attribute values, e.g.
            // <sonar ... configuration="{sonar.config.file}">.
            return;
        }

        if (! @file_exists($this->configuration)) {
            $message = sprintf('Cannot find configuration file [%s].', $this->configuration);
            throw new BuildException($message);
        }

        if (! @is_readable($this->configuration)) {
            $message = sprintf('Cannot read configuration file [%s].', $this->configuration);
            throw new BuildException($message);
        }

        // TODO: Maybe check file type?
    }

    /**
     *
     * @throws BuildException
     * @return void
     */
    private function validateProperties()
    {
        $this->properties = $this->parseConfigurationFile();

        foreach ($this->propertyElements as $property) {
            $name = $property->getName();
            $value = $property->getValue();

            if ($name === null || $name === '') {
                throw new BuildException('Property name must not be null or empty.');
            }

            if (array_key_exists($name, $this->properties)) {
                $message = sprintf('Property [%s] overwritten: old value [%s], new value [%s].',
                    $name,
                    $this->properties[$name],
                    $value);
                $this->log($message, Project::MSG_WARN);
            }

            $this->properties[$name] = $value;
        }

        // Check if all properties required by SonarQube Scanner are set ...
        $requiredProperties = array(
            'sonar.projectKey',
            'sonar.projectName',
            'sonar.projectVersion',
            'sonar.sources'
        );
        $intersection = array_intersect($requiredProperties, array_keys($this->properties));
        if (count($intersection) < count($requiredProperties)) {
            $message = 'SonarQube Scanner misses some parameters. The following properties are mandatory: ' . implode(', ', $requiredProperties) . '.';
            throw new BuildException($message);
        }
    }

    /**
     *
     * @return array
     */
    private function parseConfigurationFile() {
        if (($this->configuration === null) || ($this->configuration === '')) {
            return array();
        }

        $parser = new SonarConfigurationFileParser($this->configuration, $this->project);
        return $parser->parse();
    }

    /**
     *
     * @return boolean
     */
    private function isWindows()
    {
        $operatingSystemName = php_uname('s');
        return strtoupper(substr($operatingSystemName, 0, 3)) === 'WIN';
    }
}
<?php
/*
 *  $Id: 34d699a555092a5473d0636c5e45232dc2019c1d $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Copy files to and from a remote host using scp.
 *
 * @author    Michiel Rook <mrook@php.net>
 * @author    Johan Van den Brande <johan@vandenbrande.com>
 * @version   $Id: 34d699a555092a5473d0636c5e45232dc2019c1d $
 * @package   phing.tasks.ext
 */

class ScpTask extends Task
{
    protected $file = "";
    protected $filesets = array(); // all fileset objects assigned to this task
    protected $todir = "";
    protected $mode = null;

    protected $host = "";
    protected $port = 22;
    protected $methods = null;
    protected $username = "";
    protected $password = "";
    protected $autocreate = true;
    protected $fetch = false;
    protected $localEndpoint = "";
    protected $remoteEndpoint = "";

    protected $pubkeyfile = '';
    protected $privkeyfile = '';
    protected $privkeyfilepassphrase = '';

    protected $connection = null;
    protected $sftp = null;

    protected $counter = 0;

    protected $logLevel = Project::MSG_VERBOSE;

    /**
     * If number of success of "sftp" is grater than declared number
     * decide to skip "scp" operation.
     *
     * @var int
     */
    protected $heuristicDecision = 5;

    /**
     * Indicate number of failures in sending files via "scp" over "sftp"
     *
     * - If number is negative - scp & sftp failed
     * - If number is positive - scp failed & sftp succeed
     * - If number is 0 - scp succeed
     *
     * @var integer
     */
    protected $heuristicScpSftp = 0;

    /**
     * Sets the remote host
     * @param $h
     */
    public function setHost($h)
    {
        $this->host = $h;
    }

    /**
     * Returns the remote host
     */
    public function getHost()
    {
        return $this->host;
    }

    /**
     * Sets the remote host port
     * @param $p
     */
    public function setPort($p)
    {
        $this->port = $p;
    }

    /**
     * Returns the remote host port
     */
    public function getPort()
    {
        return $this->port;
    }

    /**
     * Sets the mode value
     * @param $value
     */
    public function setMode($value)
    {
        $this->mode = $value;
    }

    /**
     * Returns the mode value
     */
    public function getMode()
    {
        return $this->mode;
    }

    /**
     * Sets the username of the user to scp
     * @param $username
     */
    public function setUsername($username)
    {
        $this->username = $username;
    }

    /**
     * Returns the username
     */
    public function getUsername()
    {
        return $this->username;
    }

    /**
     * Sets the password of the user to scp
     * @param $password
     */
    public function setPassword($password)
    {
        $this->password = $password;
    }

    /**
     * Returns the password
     */
    public function getPassword()
    {
        return $this->password;
    }

    /**
     * Sets the public key file of the user to scp
     * @param $pubkeyfile
     */
    public function setPubkeyfile($pubkeyfile)
    {
        $this->pubkeyfile = $pubkeyfile;
    }

    /**
     * Returns the pubkeyfile
     */
    public function getPubkeyfile()
    {
        return $this->pubkeyfile;
    }

    /**
     * Sets the private key file of the user to scp
     * @param $privkeyfile
     */
    public function setPrivkeyfile($privkeyfile)
    {
        $this->privkeyfile = $privkeyfile;
    }

    /**
     * Returns the private keyfile
     */
    public function getPrivkeyfile()
    {
        return $this->privkeyfile;
    }

    /**
     * Sets the private key file passphrase of the user to scp
     * @param $privkeyfilepassphrase
     */
    public function setPrivkeyfilepassphrase($privkeyfilepassphrase)
    {
        $this->privkeyfilepassphrase = $privkeyfilepassphrase;
    }

    /**
     * Returns the private keyfile passphrase
     * @param $privkeyfilepassphrase
     * @return string
     */
    public function getPrivkeyfilepassphrase($privkeyfilepassphrase)
    {
        return $this->privkeyfilepassphrase;
    }

    /**
     * Sets whether to autocreate remote directories
     * @param $autocreate
     */
    public function setAutocreate($autocreate)
    {
        $this->autocreate = (bool) $autocreate;
    }

    /**
     * Returns whether to autocreate remote directories
     */
    public function getAutocreate()
    {
        return $this->autocreate;
    }

    /**
     * Set destination directory
     * @param $todir
     */
    public function setTodir($todir)
    {
        $this->todir = $todir;
    }

    /**
     * Returns the destination directory
     */
    public function getTodir()
    {
        return $this->todir;
    }

    /**
     * Sets local filename
     * @param $file
     */
    public function setFile($file)
    {
        $this->file = $file;
    }

    /**
     * Returns local filename
     */
    public function getFile()
    {
        return $this->file;
    }

    /**
     * Sets whether to send (default) or fetch files
     * @param $fetch
     */
    public function setFetch($fetch)
    {
        $this->fetch = (bool) $fetch;
    }

    /**
     * Returns whether to send (default) or fetch files
     */
    public function getFetch()
    {
        return $this->fetch;
    }

    /**
     * Declare number of successful operations above which "sftp" will be chosen over "scp".
     *
     * @param int $heuristicDecision Number
     */
    public function setHeuristicDecision($heuristicDecision)
    {
        $this->heuristicDecision = (int) $heuristicDecision;
    }

    /**
     * Get declared number of successful operations above which "sftp" will be chosen over "scp".
     *
     * @return int
     */
    public function getHeuristicDecision()
    {
        return $this->heuristicDecision;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Creates an Ssh2MethodParam object. Handles the <sshconfig /> nested tag
     * @return Ssh2MethodParam
     */
    public function createSshconfig()
    {
        $this->methods = new Ssh2MethodParam();

        return $this->methods;
    }

    /**
     * Set level of log messages generated (default = verbose)
     * @param string $level
     */
    public function setLevel($level)
    {
        switch ($level) {
            case "error":
                $this->logLevel = Project::MSG_ERR;
                break;
            case "warning":
                $this->logLevel = Project::MSG_WARN;
                break;
            case "info":
                $this->logLevel = Project::MSG_INFO;
                break;
            case "verbose":
                $this->logLevel = Project::MSG_VERBOSE;
                break;
            case "debug":
                $this->logLevel = Project::MSG_DEBUG;
                break;
        }
    }

    public function init()
    {
    }

    public function main()
    {
        $p = $this->getProject();

        if (!function_exists('ssh2_connect')) {
            throw new BuildException("To use ScpTask, you need to install the PHP SSH2 extension.");
        }

        if ($this->file == "" && empty($this->filesets)) {
            throw new BuildException("Missing either a nested fileset or attribute 'file'");
        }

        if ($this->host == "" || $this->username == "") {
            throw new BuildException("Attribute 'host' and 'username' must be set");
        }

        $methods = !empty($this->methods) ? $this->methods->toArray($p) : array();
        $this->connection = ssh2_connect($this->host, $this->port, $methods);
        if (!$this->connection) {
            throw new BuildException("Could not establish connection to " . $this->host . ":" . $this->port . "!");
        }

        $could_auth = null;
        if ($this->pubkeyfile) {
            $could_auth = ssh2_auth_pubkey_file(
                $this->connection,
                $this->username,
                $this->pubkeyfile,
                $this->privkeyfile,
                $this->privkeyfilepassphrase
            );
        } else {
            $could_auth = ssh2_auth_password($this->connection, $this->username, $this->password);
        }
        if (!$could_auth) {
            throw new BuildException("Could not authenticate connection!");
        }

        // prepare sftp resource
        if ($this->autocreate) {
            $this->sftp = ssh2_sftp($this->connection);
        }

        if ($this->file != "") {
            $this->copyFile($this->file, basename($this->file));
        } else {
            if ($this->fetch) {
                throw new BuildException("Unable to use filesets to retrieve files from remote server");
            }

            foreach ($this->filesets as $fs) {
                $ds = $fs->getDirectoryScanner($this->project);
                $files = $ds->getIncludedFiles();
                $dir = $fs->getDir($this->project)->getPath();
                foreach ($files as $file) {
                    $path = $dir . DIRECTORY_SEPARATOR . $file;

                    // Translate any Windows paths
                    $this->copyFile($path, strtr($file, '\\', '/'));
                }
            }
        }

        $this->log(
            "Copied " . $this->counter . " file(s) " . ($this->fetch ? "from" : "to") . " '" . $this->host . "'"
        );

        // explicitly close ssh connection
        @ssh2_exec($this->connection, 'exit');
    }

    /**
     * @param $local
     * @param $remote
     * @throws BuildException
     */
    protected function copyFile($local, $remote)
    {
        $path = rtrim($this->todir, "/") . "/";

        if ($this->fetch) {
            $localEndpoint = $path . $remote;
            $remoteEndpoint = $local;

            $this->log('Will fetch ' . $remoteEndpoint . ' to ' . $localEndpoint, $this->logLevel);

            $ret = @ssh2_scp_recv($this->connection, $remoteEndpoint, $localEndpoint);

            if ($ret === false) {
                throw new BuildException("Could not fetch remote file '" . $remoteEndpoint . "'");
            }
        } else {
            $localEndpoint = $local;
            $remoteEndpoint = $path . $remote;

            if ($this->autocreate) {
                ssh2_sftp_mkdir(
                    $this->sftp,
                    dirname($remoteEndpoint),
                    (is_null($this->mode) ? 0777 : $this->mode),
                    true
                );
            }

            $this->log('Will copy ' . $localEndpoint . ' to ' . $remoteEndpoint, $this->logLevel);

            $ret = false;
            // If more than "$this->heuristicDecision" successfully send files by "ssh2.sftp" over "ssh2_scp_send"
            // then ship this step (task finish ~40% faster)
            if ($this->heuristicScpSftp < $this->heuristicDecision) {
                if (null !== $this->mode) {
                    $ret = @ssh2_scp_send($this->connection, $localEndpoint, $remoteEndpoint, $this->mode);
                } else {
                    $ret = @ssh2_scp_send($this->connection, $localEndpoint, $remoteEndpoint);
                }
            }

            // sometimes remote server allow only create files via sftp (eg. phpcloud.com)
            if (false === $ret && $this->sftp) {
                // mark failure of "scp"
                --$this->heuristicScpSftp;

                // try create file via ssh2.sftp://file wrapper
                $fh = @fopen("ssh2.sftp://$this->sftp/$remoteEndpoint", 'wb');
                if (is_resource($fh)) {
                    $ret = fwrite($fh, file_get_contents($localEndpoint));
                    fclose($fh);

                    // mark success of "sftp"
                    $this->heuristicScpSftp += 2;
                }
            }

            if ($ret === false) {
                throw new BuildException("Could not create remote file '" . $remoteEndpoint . "'");
            }
        }

        $this->counter++;
    }
}
<?php
/*
 *  $Id: 121e04668c7aa65d641eb261fb93d3a9ece1e730 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Class that holds parameters for an ssh2_connect $methods parameter
 * This corresponds to the client_to_server and server_to_client keys of the optional $methods parameter
 * for the ssh2_connect function
 * @see http://php.net/ssh2_connect
 *
 * @author Derek Gallo <http://github.com/drock>
 *
 * @package   phing.tasks.ext
 */
class Ssh2MethodConnectionParam
{
    /**
     * @var string
     */
    private $crypt = null;

    /**
     * @var string
     */
    private $comp = null;

    /**
     * @var string
     */
    private $mac = null;

    /**
     * @param string $comp
     */
    public function setComp($comp)
    {
        $this->comp = $comp;
    }

    /**
     * @return string
     */
    public function getComp()
    {
        return $this->comp;
    }

    /**
     * @param string $crypt
     */
    public function setCrypt($crypt)
    {
        $this->crypt = $crypt;
    }

    /**
     * @return string
     */
    public function getCrypt()
    {
        return $this->crypt;
    }

    /**
     * @param string $mac
     */
    public function setMac($mac)
    {
        $this->mac = $mac;
    }

    /**
     * @return string
     */
    public function getMac()
    {
        return $this->mac;
    }

    /**
     * Get the params as an array
     * unset/null params are excluded from the array
     * @return array
     */
    public function toArray()
    {
        return array_filter(
            get_object_vars($this),
            array($this, '_filterParam')
        );
    }

    /**
     * @param $var
     * @return bool
     */
    protected function _filterParam($var)
    {
        return !is_null($var);
    }
}
<?php
/*
 *  $Id: 9356d19c55fef22aba2dcda0fff929690af41a94 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/DataType.php';
require_once 'Ssh2MethodConnectionParam.php';

/**
 * Class that holds parameters for an ssh2_connect $methods parameter
 * This corresponds to the optional $methods parameter
 * for the ssh2_connect function
 * @see http://php.net/ssh2_connect
 *
 * @author Derek Gallo <http://github.com/drock>
 *
 * @package   phing.tasks.ext
 */
class Ssh2MethodParam extends DataType
{
    /**
     * @var string
     */
    private $kex;

    /**
     * @var string
     */
    private $hostkey;

    /**
     * @var Ssh2MethodConnectionParam
     */
    private $client_to_server;

    /**
     * @var Ssh2MethodConnectionParam
     */
    private $server_to_client;

    /**
     * @param string $hostkey
     */
    public function setHostkey($hostkey)
    {
        $this->hostkey = $hostkey;
    }

    /**
     * @param Project $p
     * @throws BuildException
     * @return string
     */
    public function getHostkey(Project $p)
    {
        if ($this->isReference()) {
            return $this->getRef($p)->getHostkey($p);
        }

        return $this->hostkey;
    }

    /**
     * @param string $kex
     */
    public function setKex($kex)
    {
        $this->kex = $kex;
    }

    /**
     * @param Project $p
     * @throws BuildException
     * @return string
     */
    public function getKex(Project $p)
    {
        if ($this->isReference()) {
            return $this->getRef($p)->getKex($p);
        }

        return $this->kex;
    }

    /**
     * @param Project $p
     * @throws BuildException
     * @return \Ssh2MethodConnectionParam
     */
    public function getClientToServer(Project $p)
    {
        if ($this->isReference()) {
            return $this->getRef($p)->getClientToServer($p);
        }

        return $this->client_to_server;
    }

    /**
     * @param Project $p
     * @throws BuildException
     * @return \Ssh2MethodConnectionParam
     */
    public function getServerToClient(Project $p)
    {
        if ($this->isReference()) {
            return $this->getRef($p)->getServerToClient($p);
        }

        return $this->server_to_client;
    }

    /**
     * Handles the <client /> nested element
     * @return Ssh2MethodConnectionParam
     */
    public function createClient()
    {
        $this->client_to_server = new Ssh2MethodConnectionParam();

        return $this->client_to_server;
    }

    /**
     * Handles the <server /> nested element
     * @return Ssh2MethodConnectionParam
     */
    public function createServer()
    {
        $this->server_to_client = new Ssh2MethodConnectionParam();

        return $this->server_to_client;
    }

    /**
     * Convert the params to an array that is suitable to be passed in the ssh2_connect $methods parameter
     * @param Project $p
     * @return array
     */
    public function toArray(Project $p)
    {
        $client_to_server = $this->getClientToServer($p);
        $server_to_client = $this->getServerToClient($p);

        $array = array(
            'kex' => $this->getKex($p),
            'hostkey' => $this->getHostkey($p),
            'client_to_server' => !is_null($client_to_server) ? $client_to_server->toArray() : null,
            'server_to_client' => !is_null($server_to_client) ? $server_to_client->toArray() : null
        );

        return array_filter($array, array($this, '_filterParam'));
    }

    /**
     * @param $var
     * @return boolean
     */
    protected function _filterParam($var)
    {
        if (is_array($var)) {
            return !empty($var);
        }

        return !is_null($var);
    }

    /**
     *
     * @param Project $p
     * @throws BuildException
     * @return Ssh2MethodParam
     */
    public function getRef(Project $p)
    {
        if (!$this->checked) {
            $stk = array();
            array_push($stk, $this);
            $this->dieOnCircularReference($stk, $p);
        }
        $o = $this->ref->getReferencedObject($p);
        if (!($o instanceof Ssh2MethodParam)) {
            throw new BuildException($this->ref->getRefId() . " doesn't denote a Ssh2MethodParam");
        } else {
            return $o;
        }
    }

}
<?php
/*
 *  $Id: fcc9b201c6ff1159698d67f5966f6dd39c7591c7 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'Ssh2MethodParam.php';

/**
 * Execute commands on a remote host using ssh.
 *
 * @author    Johan Van den Brande <johan@vandenbrande.com>
 * @version   $Id: fcc9b201c6ff1159698d67f5966f6dd39c7591c7 $
 * @package   phing.tasks.ext
 */
class SshTask extends Task
{

    private $host = "";
    private $port = 22;
    private $methods = null;
    private $username = "";
    private $password = "";
    private $command = "";
    private $pubkeyfile = '';
    private $privkeyfile = '';
    private $privkeyfilepassphrase = '';
    private $pty = '';
    private $failonerror = false;

    /**
     * The name of the property to capture (any) output of the command
     * @var string
     */
    private $property = "";

    /**
     * Whether to display the output of the command
     * @var boolean
     */
    private $display = true;

    /**
     * @param $host
     */
    public function setHost($host)
    {
        $this->host = $host;
    }

    /**
     * @return string
     */
    public function getHost()
    {
        return $this->host;
    }

    /**
     * @param $port
     */
    public function setPort($port)
    {
        $this->port = $port;
    }

    /**
     * @return int
     */
    public function getPort()
    {
        return $this->port;
    }

    /**
     * @param $username
     */
    public function setUsername($username)
    {
        $this->username = $username;
    }

    /**
     * @return string
     */
    public function getUsername()
    {
        return $this->username;
    }

    /**
     * @param $password
     */
    public function setPassword($password)
    {
        $this->password = $password;
    }

    /**
     * @return string
     */
    public function getPassword()
    {
        return $this->password;
    }

    /**
     * Sets the public key file of the user to scp
     * @param $pubkeyfile
     */
    public function setPubkeyfile($pubkeyfile)
    {
        $this->pubkeyfile = $pubkeyfile;
    }

    /**
     * Returns the pubkeyfile
     */
    public function getPubkeyfile()
    {
        return $this->pubkeyfile;
    }

    /**
     * Sets the private key file of the user to scp
     * @param $privkeyfile
     */
    public function setPrivkeyfile($privkeyfile)
    {
        $this->privkeyfile = $privkeyfile;
    }

    /**
     * Returns the private keyfile
     */
    public function getPrivkeyfile()
    {
        return $this->privkeyfile;
    }

    /**
     * Sets the private key file passphrase of the user to scp
     * @param $privkeyfilepassphrase
     */
    public function setPrivkeyfilepassphrase($privkeyfilepassphrase)
    {
        $this->privkeyfilepassphrase = $privkeyfilepassphrase;
    }

    /**
     * Returns the private keyfile passphrase
     * @param $privkeyfilepassphrase
     * @return string
     */
    public function getPrivkeyfilepassphrase($privkeyfilepassphrase)
    {
        return $this->privkeyfilepassphrase;
    }

    /**
     * @param $command
     */
    public function setCommand($command)
    {
        $this->command = $command;
    }

    /**
     * @return string
     */
    public function getCommand()
    {
        return $this->command;
    }

    /**
     * @param $pty
     */
    public function setPty($pty)
    {
        $this->pty = $pty;
    }

    /**
     * @return string
     */
    public function getPty()
    {
        return $this->pty;
    }

    /**
     * Sets the name of the property to capture (any) output of the command
     * @param string $property
     */
    public function setProperty($property)
    {
        $this->property = $property;
    }

    /**
     * Sets whether to display the output of the command
     * @param boolean $display
     */
    public function setDisplay($display)
    {
        $this->display = (boolean) $display;
    }

    /**
     * Sets whether to fail the task on any error
     * @param $failonerror
     * @internal param bool $failOnError
     */
    public function setFailonerror($failonerror)
    {
        $this->failonerror = (boolean) $failonerror;
    }

    /**
     * Creates an Ssh2MethodParam object. Handles the <sshconfig /> nested tag
     * @return Ssh2MethodParam
     */
    public function createSshconfig()
    {
        $this->methods = new Ssh2MethodParam();

        return $this->methods;
    }

    public function init()
    {
    }

    /**
     * Initiates a ssh connection and stores
     * it in $this->connection
     */
    protected function setupConnection()
    {
        $p = $this->getProject();

        if (!function_exists('ssh2_connect')) {
            throw new BuildException("To use SshTask, you need to install the PHP SSH2 extension.");
        }

        $methods = !empty($this->methods) ? $this->methods->toArray($p) : array();
        $this->connection = ssh2_connect($this->host, $this->port, $methods);
        if (!$this->connection) {
            throw new BuildException("Could not establish connection to " . $this->host . ":" . $this->port . "!");
        }

        $could_auth = null;
        if ($this->pubkeyfile) {
            $could_auth = ssh2_auth_pubkey_file(
                $this->connection,
                $this->username,
                $this->pubkeyfile,
                $this->privkeyfile,
                $this->privkeyfilepassphrase
            );
        } else {
            $could_auth = ssh2_auth_password($this->connection, $this->username, $this->password);
        }
        if (!$could_auth) {
            throw new BuildException("Could not authenticate connection!");
        }
    }

    public function main()
    {
        $this->setupConnection();

        if ($this->pty != '') {
            $stream = ssh2_exec($this->connection, $this->command, $this->pty);
        } else {
            $stream = ssh2_exec($this->connection, $this->command);
        }

        $this->handleStream($stream);
    }

    /**
     * This function reads the streams from the ssh2_exec
     * command, stores output data, checks for errors and
     * closes the streams properly.
     * @param $stream
     * @throws BuildException
     */
    protected function handleStream($stream)
    {
        if (!$stream) {
            throw new BuildException("Could not execute command!");
        }

        $this->log("Executing command {$this->command}", Project::MSG_VERBOSE);

        stream_set_blocking($stream, true);
        $result = stream_get_contents($stream);

        // always load contents of error stream, to make sure not one command failed
        $stderr_stream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
        stream_set_blocking($stderr_stream, true);
        $result_error = stream_get_contents($stderr_stream);

        if ($this->display) {
            print($result);
        }

        if (!empty($this->property)) {
            $this->project->setProperty($this->property, $result);
        }

        fclose($stream);
        if (isset($stderr_stream)) {
            fclose($stderr_stream);
        }

        if ($this->failonerror && !empty($result_error)) {
            throw new BuildException("SSH Task failed: " . $result_error);
        }
    }

}
<?php
/**
 * $Id: 46a07222af7bd1fbfd0e39169d5f80a83c6525e1 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Stopwatch.
 *
 * @author Siad Ardroumli <siad.ardroumli@gmail.com>
 * @version $Id: 46a07222af7bd1fbfd0e39169d5f80a83c6525e1 $
 * @package phing.tasks.ext.stopwatch
 */
class StopwatchTask extends Task
{
    /**
     * Name of the timer.
     *
     * @var string $name
     */
    private $name = '';

    /**
     * Category of the timer.
     *
     * @var string $category optional
     */
    private $category = '';

    /**
     * Timer  action.
     *
     * @var string $action
     */
    private $action = 'start';

    /**
     * Holds an instance of Stopwatch.
     *
     * @var Stopwatch $timer
     */
    private static $timer = null;

    /**
     * Initialize Task.
     *
     * @return void
     */
    public function init()
    {
    }

    /**
     * Load stopwatch.
     *
     * @return void
     *
     * @throws BuildException
     */
    private function loadStopwatch()
    {
        if (version_compare(PHP_VERSION, '5.3.3', '<')) {
            throw new BuildException("StopwatchTask requires at least PHP 5.3.3 installed.");
        }

        @include_once 'Symfony/Component/Stopwatch/autoload.php';
        @include_once 'vendor/autoload.php';

        if (!class_exists('\\Symfony\\Component\\Stopwatch\\Stopwatch')) {
            throw new BuildException("StopwatchTask requires Stopwatch to be installed");
        }
    }

    /**
     * Get the stopwatch instance.
     *
     * @return \Symfony\Component\Stopwatch\Stopwatch
     */
    private function getStopwatchInstance()
    {
        if (self::$timer === null) {
            $stopwatch = '\\Symfony\\Component\\Stopwatch\\Stopwatch';
            self::$timer = new $stopwatch;
        }

        return self::$timer;
    }

    /**
     * Start timer.
     *
     * @return void
     */
    private function start()
    {
        $timer = $this->getStopwatchInstance();
        $timer->start($this->name, $this->category);
    }

    /**
     * Stop timer.
     *
     * @return void
     */
    private function stop()
    {
        $timer = $this->getStopwatchInstance();
        $event = $timer->stop($this->name);

        foreach ($event->getPeriods() as $period) {
            $this->log('Starttime: ' . $period->getStartTime() . ' - Endtime: ' . $period->getEndTime() . ' - Duration: ' . $period->getDuration() . ' - Memory: ' . $period->getMemory(), Project::MSG_INFO);
        }

        $this->log('Category:   ' . $event->getCategory(), Project::MSG_INFO);
        $this->log('Origin:     ' . $event->getOrigin(), Project::MSG_INFO);
        $this->log('Start time: ' . $event->getStartTime(), Project::MSG_INFO);
        $this->log('End time:   ' . $event->getEndTime(), Project::MSG_INFO);
        $this->log('Duration:   ' . $event->getDuration(), Project::MSG_INFO);
        $this->log('Memory:     ' . $event->getMemory(), Project::MSG_INFO);
    }

    /**
     * Measure lap time.
     *
     * @return void
     */
    private function lap()
    {
        $timer = $this->getStopwatchInstance();
        $timer->lap($this->name);
    }

    /**
     * Set the name of the stopwatch.
     *
     * @param string $name the name of the stopwatch timer
     *
     * @return void
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Set the category of the stopwatch.
     *
     * @param string $category
     *
     * @return void
     */
    public function setCategory($category)
    {
        $this->category = $category;
    }

    /**
     * Set the action.
     * Action could be one of
     * - start
     * - lap
     * - stop
     *
     * @param string $action
     *
     * @return void
     */
    public function setAction($action)
    {
        $this->action = $action;
    }

    /**
     * The main entry point
     *
     * @return void
     *
     * @throws BuildException
     */
    public function main()
    {
        $this->loadStopwatch();

        switch ($this->action) {
            case "start":
                $this->start();
                break;
            case "stop":
                $this->stop();
                break;
            case "lap":
                $this->lap();
                break;
            default:
                throw new BuildException('action should be one of start, stop, lap.');
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/Task.php';

/**
 * Base class for Subversion tasks
 *
 * @author Michiel Rook <mrook@php.net>
 * @author Andrew Eddie <andrew.eddie@jamboworks.com>
 *
 * @package phing.tasks.ext.svn
 *
 * @see VersionControl_SVN
 * @since 2.2.0
 */
abstract class SvnBaseTask extends Task
{
    /**
     * @var string
     */
    private $workingCopy = "";

    /**
     * @var string
     */
    private $repositoryUrl = "";

    /**
     * @var string
     */
    private $svnPath = "/usr/bin/svn";

    protected $svn = null;

    private $mode = "";

    private $svnArgs = array();

    private $svnSwitches = array();

    private $toDir = "";

    protected $fetchMode;

    protected $oldVersion = false;

    /**
     * Initialize Task.
     * This method includes any necessary SVN libraries and triggers
     * appropriate error if they cannot be found.  This is not done in header
     * because we may want this class to be loaded w/o triggering an error.
     */
    public function init()
    {
        include_once 'VersionControl/SVN.php';
        $this->fetchMode = VERSIONCONTROL_SVN_FETCHMODE_ASSOC;
        if (!class_exists('VersionControl_SVN')) {
            throw new Exception("The SVN tasks depend on PEAR VersionControl_SVN package being installed.");
        }
    }

    /**
     * Sets the path to the workingcopy
     * @param $workingCopy
     */
    public function setWorkingCopy($workingCopy)
    {
        $this->workingCopy = $workingCopy;
    }

    /**
     * Returns the path to the workingcopy
     */
    public function getWorkingCopy()
    {
        return $this->workingCopy;
    }

    /**
     * Sets the path/URI to the repository
     * @param $repositoryUrl
     */
    public function setRepositoryUrl($repositoryUrl)
    {
        $this->repositoryUrl = $repositoryUrl;
    }

    /**
     * Returns the path/URI to the repository
     */
    public function getRepositoryUrl()
    {
        return $this->repositoryUrl;
    }

    /**
     * Sets the path to the SVN executable
     * @param $svnPath
     */
    public function setSvnPath($svnPath)
    {
        $this->svnPath = $svnPath;
    }

    /**
     * Returns the path to the SVN executable
     */
    public function getSvnPath()
    {
        return $this->svnPath;
    }

    //
    // Args
    //

    /**
     * Sets the path to export/checkout to
     * @param $toDir
     */
    public function setToDir($toDir)
    {
        $this->toDir = $toDir;
    }

    /**
     * Returns the path to export/checkout to
     */
    public function getToDir()
    {
        return $this->toDir;
    }

    //
    // Switches
    //

    /**
     * Sets the force switch
     * @param $value
     */
    public function setForce($value)
    {
        $this->svnSwitches['force'] = $value;
    }

    /**
     * Returns the force switch
     */
    public function getForce()
    {
        return isset($this->svnSwitches['force']) ? $this->svnSwitches['force'] : '';
    }

    /**
     * Sets the username of the user to export
     * @param $value
     */
    public function setUsername($value)
    {
        $this->svnSwitches['username'] = $value;
    }

    /**
     * Returns the username
     */
    public function getUsername()
    {
        return isset($this->svnSwitches['username']) ? $this->svnSwitches['username'] : '';
    }

    /**
     * Sets the password of the user to export
     * @param $value
     */
    public function setPassword($value)
    {
        $this->svnSwitches['password'] = $value;
    }

    /**
     * Returns the password
     */
    public function getPassword()
    {
        return isset($this->svnSwitches['password']) ? $this->svnSwitches['password'] : '';
    }

    /**
     * Sets the no-auth-cache switch
     * @param $value
     */
    public function setNoCache($value)
    {
        $this->svnSwitches['no-auth-cache'] = $value;
    }

    /**
     * Returns the no-auth-cache switch
     */
    public function getNoCache()
    {
        return isset($this->svnSwitches['no-auth-cache']) ? $this->svnSwitches['no-auth-cache'] : '';
    }

    /**
     * Sets the recursive switch
     * @deprecated
     * @param $value
     */
    public function setRecursive($value)
    {
    }

    /**
     * Returns the recursive switch
     * @deprecated
     */
    public function getRecursive()
    {
    }

    /**
     * Sets the depth switch
     * @param $value
     */
    public function setDepth($value)
    {
        $this->svnSwitches['depth'] = $value;
    }

    /**
     * Returns the depth switch
     */
    public function getDepth()
    {
        return isset($this->svnSwitches['depth']) ? $this->svnSwitches['depth'] : '';
    }

    /**
     * Sets the ignore-externals switch
     * @param $value
     */
    public function setIgnoreExternals($value)
    {
        $this->svnSwitches['ignore-externals'] = $value;
    }

    /**
     * Returns the ignore-externals switch
     */
    public function getIgnoreExternals()
    {
        return isset($this->svnSwitches['ignore-externals']) ? $this->svnSwitches['ignore-externals'] : '';
    }

    /**
     * Sets the trust-server-cert switch
     * @param $value
     */
    public function setTrustServerCert($value)
    {
        $this->svnSwitches['trust-server-cert'] = $value;
    }

    /**
     * Returns the trust-server-cert switch
     */
    public function getTrustServerCert()
    {
        return isset($this->svnSwitches['trust-server-cert']) ? $this->svnSwitches['trust-server-cert'] : '';
    }

    /**
     * Creates a VersionControl_SVN class based on $mode
     *
     * @param string The SVN mode to use (info, export, checkout, ...)
     * @throws BuildException
     */
    protected function setup($mode)
    {
        $this->mode = $mode;

        // Set up runtime options. Will be passed to all
        // subclasses.
        $options = array('fetchmode' => $this->fetchMode);

        if ($this->oldVersion) {
            $options['svn_path'] = $this->getSvnPath();
        } else {
            $options['binaryPath'] = $this->getSvnPath();
        }

        // Pass array of subcommands we need to factory
        $this->svn = VersionControl_SVN::factory($mode, $options);

        if (get_parent_class($this->svn) !== 'VersionControl_SVN_Command') {
            $this->oldVersion = true;
            $this->svn->use_escapeshellcmd = false;
        }

        if (!empty($this->repositoryUrl)) {
            $this->svnArgs = array($this->repositoryUrl);
        } else {
            if (!empty($this->workingCopy)) {
                if (is_dir($this->workingCopy)) {
                    $this->svnArgs = array($this->workingCopy);
                } else {
                    if ($mode == 'info') {
                        if (is_file($this->workingCopy)) {
                            $this->svnArgs = array($this->workingCopy);
                        } else {
                            throw new BuildException("'" . $this->workingCopy . "' is not a directory nor a file");
                        }
                    } else {
                        throw new BuildException("'" . $this->workingCopy . "' is not a directory");
                    }
                }
            }
        }
    }

    /**
     * Executes the constructed VersionControl_SVN instance
     *
     * @param array $args
     * @param array $switches
     * @throws BuildException
     * @internal param Additional $array arguments to pass to SVN.
     * @internal param Switches $array to pass to SVN.
     * @return string Output generated by SVN.
     */
    protected function run($args = array(), $switches = array())
    {
        $tempArgs = array_merge($this->svnArgs, $args);
        $tempSwitches = array_merge($this->svnSwitches, $switches);

        if ($this->oldVersion) {
            $svnstack = PEAR_ErrorStack::singleton('VersionControl_SVN');

            if ($output = $this->svn->run($tempArgs, $tempSwitches)) {
                return $output;
            }

            if (count($errs = $svnstack->getErrors())) {
                $err = current($errs);
                $errorMessage = $err['message'];

                if (isset($err['params']['errstr'])) {
                    $errorMessage = $err['params']['errstr'];
                }

                throw new BuildException("Failed to run the 'svn " . $this->mode . "' command: " . $errorMessage);
            }
        } else {
            try {
                return $this->svn->run($tempArgs, $tempSwitches);
            } catch (Exception $e) {
                throw new BuildException("Failed to run the 'svn " . $this->mode . "' command: " . $e->getMessage());
            }
        }
    }
}
<?php
/**
 * $Id: 34178e3af0b03b5da591c716f43bbc49a25cdcb4 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/svn/SvnBaseTask.php';

/**
 * Checks out a repository to a local directory
 *
 * @author Andrew Eddie <andrew.eddie@jamboworks.com>
 * @version $Id: 34178e3af0b03b5da591c716f43bbc49a25cdcb4 $
 * @package phing.tasks.ext.svn
 * @since 2.3.0
 */
class SvnCheckoutTask extends SvnBaseTask
{
    /**
     * Which Revision to Export
     *
     * @todo check if version_control_svn supports constants
     *
     * @var string
     */
    private $revision = 'HEAD';

    /**
     * The main entry point
     *
     * @throws BuildException
     */
    public function main()
    {
        $this->setup('checkout');

        $this->log(
            "Checking out SVN repository to '" . $this->getToDir(
            ) . "'" . ($this->revision == 'HEAD' ? '' : " (revision: {$this->revision})")
        );

        // revision
        $switches = array(
            'r' => $this->revision,
        );

        $this->run(array($this->getToDir()), $switches);
    }

    /**
     * @param $revision
     */
    public function setRevision($revision)
    {
        $this->revision = $revision;
    }
}
<?php
/**
 * $Id: eec7114955b928683994705ad4dc68fe246ef23f $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/svn/SvnBaseTask.php';

/**
 * Commits changes in a local working copy to the repository
 *
 * @author Johan Persson <johanp@aditus.nu>
 * @version $Id: eec7114955b928683994705ad4dc68fe246ef23f $
 * @package phing.tasks.ext.svn
 * @since 2.4.0
 */
class SvnCommitTask extends SvnBaseTask
{
    /**
     * Commit message
     */
    private $message = '';

    /**
     * Property name where we store the revision number of the just
     * committed version.
     */
    private $propertyName = "svn.committedrevision";

    /**
     * Sets the commit message
     * @param $message
     */
    public function setMessage($message)
    {
        $this->message = $message;
    }

    /**
     * Gets the commit message
     */
    public function getMessage()
    {
        return $this->message;
    }

    /**
     * Sets the name of the property to use for returned revision
     * @param $propertyName
     */
    public function setPropertyName($propertyName)
    {
        $this->propertyName = $propertyName;
    }

    /**
     * Returns the name of the property to use for returned revision
     */
    public function getPropertyName()
    {
        return $this->propertyName;
    }

    /**
     * The main entry point
     *
     * @throws BuildException
     */
    public function main()
    {
        if (trim($this->message) === '') {
            throw new BuildException('SVN Commit message can not be empty.');
        }

        $this->setup('commit');

        $this->log(
            "Committing SVN working copy at '" . $this->getWorkingCopy() . "' with message '" . $this->GetMessage() . "'"
        );

        $output = $this->run(array(), array('message' => $this->GetMessage()));

        if (preg_match('/[\s]*Committed revision[\s]+([\d]+)/', $output, $matches)) {
            $this->project->setProperty($this->getPropertyName(), $matches[1]);
        } else {
            /**
             * If no new revision was committed set revision to "empty". Remember that
             * this is not necessarily an error. It could be that the specified working
             * copy is identical to the copy in the repository and in that case
             * there will be no update and no new revision number.
             */
            $this->project->setProperty($this->getPropertyName(), '');
        }

    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/svn/SvnBaseTask.php';

/**
 * Copies a repository from the repository url to another
 *
 * @version $Id: 0b4894341e10f5f3ba44a669707b7727b58383f5 $
 * @package phing.tasks.ext.svn
 * @since 2.3.0
 */
class SvnCopyTask extends SvnBaseTask
{
    private $message = "";

    /**
     * Sets the message
     * @param $message
     */
    public function setMessage($message)
    {
        $this->message = $message;
    }

    /**
     * Gets the message
     */
    public function getMessage()
    {
        return $this->message;
    }

    /**
     * The main entry point
     *
     * @throws BuildException
     */
    public function main()
    {
        $this->setup('copy');

        $this->log("Copying SVN repository from '" . $this->getRepositoryUrl() . "' to '" . $this->getToDir() . "'");

        $options = array();

        if (strlen($this->getMessage()) > 0) {
            $options['message'] = $this->getMessage();
        }

        $this->run(array($this->getToDir()), $options);
    }
}
<?php
/**
 * $Id: d12b9c4d44e42e6a36e544fbb0f03c0bd64ec428 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/svn/SvnBaseTask.php';

/**
 * Exports/checks out a repository to a local directory
 * with authentication
 *
 * @author Michiel Rook <mrook@php.net>
 * @author Andrew Eddie <andrew.eddie@jamboworks.com>
 * @version $Id: d12b9c4d44e42e6a36e544fbb0f03c0bd64ec428 $
 * @package phing.tasks.ext.svn
 * @since 2.2.0
 */
class SvnExportTask extends SvnBaseTask
{
    /**
     * Which Revision to Export
     *
     * @todo check if version_control_svn supports constants
     *
     * @var string
     */
    private $revision = 'HEAD';

    /**
     * The main entry point
     *
     * @throws BuildException
     */
    public function main()
    {
        $this->setup('export');

        $this->log("Exporting SVN repository to '" . $this->getToDir() . "'");

        $switches = array();

        if (!empty($this->revision)) {
            $switches['r'] = $this->revision;
        }

        $this->run(array($this->getToDir()), $switches);
    }

    /**
     * @param $revision
     */
    public function setRevision($revision)
    {
        $this->revision = $revision;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/svn/SvnBaseTask.php';

/**
 * Parses the output of 'svn info --xml' and
 *
 * @author Michiel Rook <mrook@php.net>
 *
 * @package phing.tasks.ext.svn
 *
 * @see VersionControl_SVN
 * @since 2.4.9
 */
class SvnInfoTask extends SvnBaseTask
{
    private $propertyName = "svn.info";

    private $element = 'url';
    private $subElement = null;

    /**
     * Sets the name of the property to use
     * @param $propertyName
     */
    public function setPropertyName($propertyName)
    {
        $this->propertyName = $propertyName;
    }

    /**
     * Returns the name of the property to use
     */
    public function getPropertyName()
    {
        return $this->propertyName;
    }

    /**
     * Sets the name of the xml element to use.
     *
     * @param string $element
     *
     * @return void
     */
    public function setElement($element)
    {
        $this->element = $element;
    }

    /**
     * Returns the name of the xml element to use.
     *
     * @return string
     */
    public function getElement()
    {
        return $this->element;
    }

    /**
     * Sets the name of the xml sub element to use.
     *
     * @param $subElement
     *
     * @return void
     */
    public function setSubElement($subElement)
    {
        $this->subElement = $subElement;
    }

    /**
     * Returns the name of the xml sub element to use.
     *
     * @return string
     */
    public function getSubElement()
    {
        return $this->subElement;
    }

    /**
     * The main entry point.
     *
     * @return void
     *
     * @throws BuildException
     */
    public function main()
    {
        $this->setup('info');

        if ($this->oldVersion) {
            $output = $this->run(array('--xml', '--incremental'));

            if (!($xmlObj = @simplexml_load_string($output))) {
                throw new BuildException("Failed to parse the output of 'svn info --xml'.");
            }

            $object = $xmlObj->{$this->element};

            if (!empty($this->subElement)) {
                $object = $object->{$this->subElement};
            }
        } else {
            $output = $this->run();

            if (empty($output) || !isset($output['entry'][0])) {
                throw new BuildException("Failed to parse the output of 'svn info'.");
            }

            $object = $output['entry'][0][$this->element];

            if (!empty($this->subElement)) {
                $object = $object[$this->subElement];
            }
        }

        $this->project->setProperty($this->getPropertyName(), (string) $object);
    }
}
<?php
/**
 * $Id: 61bd055f46bd8430785610a76271250d2e30786e $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/svn/SvnBaseTask.php';

/**
 * Stores the number of the last revision of a workingcopy in a property
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 61bd055f46bd8430785610a76271250d2e30786e $
 * @package phing.tasks.ext.svn
 * @see VersionControl_SVN
 * @since 2.1.0
 */
class SvnLastRevisionTask extends SvnBaseTask
{
    private $propertyName = "svn.lastrevision";
    private $lastChanged = false;

    /**
     * Sets the name of the property to use
     * @param string $propertyName
     */
    public function setPropertyName($propertyName)
    {
        $this->propertyName = $propertyName;
    }

    /**
     * Returns the name of the property to use
     * @return string
     */
    public function getPropertyName()
    {
        return $this->propertyName;
    }

    /**
     * Sets whether to force compatibility with older SVN versions (< 1.2)
     *
     * Retained for legacy reasons
     * @deprecated
     * @param $force
     */
    public function setForceCompatible($force)
    {
    }

    /**
     * Sets whether to retrieve the last changed revision
     * @param $lastChanged
     */
    public function setLastChanged($lastChanged)
    {
        $this->lastChanged = (bool) $lastChanged;
    }

    /**
     * The main entry point
     *
     * @throws BuildException
     */
    public function main()
    {
        $this->setup('info');

        if ($this->oldVersion) {
            $output = $this->run(array('--xml'));

            if (!($xmlObj = @simplexml_load_string($output))) {
                throw new BuildException("Failed to parse the output of 'svn info --xml'.");
            }

            if ($this->lastChanged) {
                $found = (int) $xmlObj->entry->commit['revision'];
            } else {
                $found = (int) $xmlObj->entry['revision'];
            }
        } else {
            $output = $this->run();

            if (empty($output) || !isset($output['entry'][0])) {
                throw new BuildException("Failed to parse the output of 'svn info'.");
            }

            if ($this->lastChanged) {
                $found = $output['entry'][0]['commit']['revision'];
            } else {
                $found = $output['entry'][0]['revision'];
            }
        }

        $this->project->setProperty($this->getPropertyName(), $found);
    }
}
<?php
/**
 * $Id: bb6bda1d445fc14e863a2c88b2d11acaebe757c0 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/svn/SvnBaseTask.php';

/**
 * Stores the output of a list command on a workingcopy or repositoryurl in a property.
 * This stems from the SvnLastRevisionTask.
 *
 * @author Anton Stöckl <anton@stoeckl.de>
 * @author Michiel Rook <mrook@php.net> (SvnLastRevisionTask)
 * @version $Id: bb6bda1d445fc14e863a2c88b2d11acaebe757c0 $
 * @package phing.tasks.ext.svn
 * @see VersionControl_SVN
 * @since 2.1.0
 */
class SvnListTask extends SvnBaseTask
{
    private $propertyName = "svn.list";
    private $limit = null;
    private $orderDescending = false;

    /**
     * Sets the name of the property to use
     * @param $propertyName
     */
    public function setPropertyName($propertyName)
    {
        $this->propertyName = $propertyName;
    }

    /**
     * Returns the name of the property to use
     */
    public function getPropertyName()
    {
        return $this->propertyName;
    }

    /**
     * Sets whether to force compatibility with older SVN versions (< 1.2)
     * @deprecated
     * @param $force
     */
    public function setForceCompatible($force)
    {
    }

    /**
     * Sets the max num of tags to display
     * @param $limit
     */
    public function setLimit($limit)
    {
        $this->limit = (int) $limit;
    }

    /**
     * Sets whether to sort tags in descending order
     * @param $orderDescending
     */
    public function setOrderDescending($orderDescending)
    {
        $this->orderDescending = (bool) $orderDescending;
    }

    /**
     * The main entry point
     *
     * @throws BuildException
     */
    public function main()
    {
        $this->setup('list');

        if ($this->oldVersion) {
            $this->svn->setOptions(array('fetchmode' => VERSIONCONTROL_SVN_FETCHMODE_XML));
            $output = $this->run(array('--xml'));

            if (!($xmlObj = @simplexml_load_string($output))) {
                throw new BuildException("Failed to parse the output of 'svn list --xml'.");
            }

            $objects = $xmlObj->list->entry;
            $entries = array();

            foreach ($objects as $object) {
                $entries[] = array(
                    'commit' => array(
                        'revision' => (string) $object->commit['revision'],
                        'author' => (string) $object->commit->author,
                        'date' => (string) $object->commit->date
                    ),
                    'name' => (string) $object->name
                );
            }
        } else {
            $output = $this->run(array());
            $entries = $output['list'][0]['entry'];
        }

        if ($this->orderDescending) {
            $entries = array_reverse($entries);
        }

        $result = null;
        $count = 0;

        foreach ($entries as $entry) {
            if ($this->limit > 0 && $count >= $this->limit) {
                break;
            }

            $result .= (!empty($result)) ? "\n" : '';
            $result .= $entry['commit']['revision'] . ' | ' . $entry['commit']['author'] . ' | ' . $entry['commit']['date'] . ' | ' . $entry['name'];
            $count++;
        }

        if (!empty($result)) {
            $this->project->setProperty($this->getPropertyName(), $result);
        } else {
            throw new BuildException("Failed to parse the output of 'svn list'.");
        }
    }
}
<?php
/**
 * $Id: 7e1ed209fca1c776aa105241110a78a65284f6ae $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/svn/SvnBaseTask.php';

/**
 * Stores the output of a log command on a workingcopy or repositoryurl in a property.
 * This stems from the SvnLastRevisionTask.
 *
 * @author Anton Stöckl <anton@stoeckl.de>
 * @author Michiel Rook <mrook@php.net> (SvnLastRevisionTask)
 * @version $Id: 7e1ed209fca1c776aa105241110a78a65284f6ae $
 * @package phing.tasks.ext.svn
 * @see VersionControl_SVN
 * @since 2.1.0
 */
class SvnLogTask extends SvnBaseTask
{
    private $propertyName = "svn.log";
    private $limit = null;

    /**
     * Sets the name of the property to use
     * @param $propertyName
     */
    public function setPropertyName($propertyName)
    {
        $this->propertyName = $propertyName;
    }

    /**
     * Returns the name of the property to use
     */
    public function getPropertyName()
    {
        return $this->propertyName;
    }

    /**
     * Sets whether to force compatibility with older SVN versions (< 1.2)
     * @deprecated
     * @param $force
     */
    public function setForceCompatible($force)
    {
    }

    /**
     * Sets the max num of log entries to get from svn
     * @param $limit
     */
    public function setLimit($limit)
    {
        $this->limit = (int) $limit;
    }

    /**
     * The main entry point
     *
     * @throws BuildException
     */
    public function main()
    {
        $this->setup('log');

        $switches = array();
        if ($this->limit > 0) {
            $switches['limit'] = $this->limit;
        }

        $output = $this->run(array(), $switches);
        $result = null;

        if ($this->oldVersion) {
            foreach ($output as $line) {
                $result .= (!empty($result)) ? "\n" : '';
                $result .= "{$line['REVISION']} | {$line['AUTHOR']}  | {$line['DATE']}  | {$line['MSG']}";
            }
        } else {
            foreach ($output['logentry'] as $line) {
                $result .= (!empty($result)) ? "\n" : '';
                $result .= "{$line['revision']} | {$line['author']}  | {$line['date']}  | {$line['msg']}";
            }
        }

        if (!empty($result)) {
            $this->project->setProperty($this->getPropertyName(), $result);
        } else {
            throw new BuildException("Failed to parse the output of 'svn log'.");
        }
    }
}
<?php
/**
 * $Id: f840cd73068d8b9cf90051b0be3101ee9f22d4fc $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/svn/SvnBaseTask.php';

/**
 * Switches a repository at a given local directory to a different location
 *
 * @author Dom Udall <dom.udall@clock.co.uk>
 * @version $Id: f840cd73068d8b9cf90051b0be3101ee9f22d4fc $
 * @package phing.tasks.ext.svn
 * @since 2.4.3
 */
class SvnSwitchTask extends SvnBaseTask
{
    /**
     * Which Revision to Export
     *
     * @todo check if version_control_svn supports constants
     *
     * @var string
     */
    private $revision = 'HEAD';

    /**
     * The main entry point
     *
     * @throws BuildException
     */
    public function main()
    {
        $this->setup('switch');

        $this->log(
            "Switching SVN repository at '" . $this->getToDir() . "' to '" . $this->getRepositoryUrl() . "' "
            . ($this->getRevision() == 'HEAD' ? '' : " (revision: {$this->getRevision()})")
        );

        // revision
        $switches = array(
            'r' => $this->getRevision(),
        );

        $this->run(array($this->getToDir()), $switches);
    }

    /**
     * @param $revision
     */
    public function setRevision($revision)
    {
        $this->revision = $revision;
    }

    /**
     * @return string
     */
    public function getRevision()
    {
        return $this->revision;
    }
}
<?php
/**
 * $Id: e29c24db4d10667a89d1829fe80075fbf8e3292c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/ext/svn/SvnBaseTask.php';

/**
 * Updates a repository in local directory
 *
 * @author Andrew Eddie <andrew.eddie@jamboworks.com>
 * @version $Id: e29c24db4d10667a89d1829fe80075fbf8e3292c $
 * @package phing.tasks.ext.svn
 * @since 2.3.0
 */
class SvnUpdateTask extends SvnBaseTask
{
    /**
     * Which Revision to Export
     *
     * @todo check if version_control_svn supports constants
     *
     * @var string
     */
    private $revision = 'HEAD';

    /**
     * The main entry point
     *
     * @throws BuildException
     */
    public function main()
    {
        $this->setup('update');

        $this->log(
            "Updating SVN repository at '" . $this->getToDir(
            ) . "'" . ($this->revision == 'HEAD' ? '' : " (revision: {$this->revision})")
        );

        // revision
        $switches = array(
            'r' => $this->revision,
        );

        $this->run(array($this->getToDir()), $switches);
    }

    /**
     * @param $revision
     */
    public function setRevision($revision)
    {
        $this->revision = $revision;
    }
}
<?php

/*
 *  $Id: 0e03453bfaa39363850a37e44da2cf435cdf66e6 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once "phing/types/DataType.php";

/**
 * Implementation of console argument
 *
 * @author nuno costa <nuno@francodacosta.com>
 * @license GPL
 * @version $Id: 0e03453bfaa39363850a37e44da2cf435cdf66e6 $
 * @package phing.tasks.ext.symfony
 */
class Arg extends DataType
{
    private $name = null;
    private $value = null;
    private $quotes = false;

    /**
     * Gets the argument name
     * @return String
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Sets the argument name
     * @param String $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Gets the argument value
     * @return String
     */
    public function getValue()
    {
        return $this->value;
    }

    /**
     * Sets the argument value
     * @param String $value
     */
    public function setValue($value)
    {
        $this->value = $value;
    }

    /**
     * Should the argument value be enclosed in double quotes
     * @return boolean
     */
    public function getQuotes()
    {
        return $this->quotes;
    }

    /**
     * Should the argument value be enclosed in double quotes
     * @param boolean $quotes
     */
    public function setQuotes($quotes)
    {
        $this->quotes = $quotes;
    }

    /**
     * Transforms the argument object into a string, takes into consideration
     * the quotes and the argument value
     * @return String
     */
    public function __toString()
    {
        $name = "";
        $value = "";
        $quote = $this->getQuotes() ? '"' : '';

        if (!is_null($this->getValue())) {
            $value = $quote . $this->getValue() . $quote;
        }

        if (!is_null($this->getName())) {
            $name = '--' . $this->getName();
        }

        if (strlen($name) > 0 && strlen($value) > 0) {
            $value = '=' . $value;
        }

        return $name . $value;
    }

}
<?php

/*
 *  $Id: e2d3229326c8a6b49e75ef4e9bf892816e26e350 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once "phing/Task.php";
require_once dirname(__FILE__) . "/Arg.php";

/**
 * Symfony Console Task
 * @author nuno costa <nuno@francodacosta.com>
 * @license GPL
 * @version $Id: e2d3229326c8a6b49e75ef4e9bf892816e26e350 $
 * @package phing.tasks.ext.symfony
 */
class SymfonyConsoleTask extends Task
{

    /**
     *
     * @var Arg[] a collection of Arg objects
     */
    private $args = array();

    /**
     *
     * @var string the Symfony console command to execute
     */
    private $command = null;

    /**
     *
     * @var string path to symfony console application
     */
    private $console = 'app/console';

    /**
     *
     * @var string property to be set
     */
    private $propertyName = null;

    /**
     * Whether to check the return code.
     * @var boolean
     */
    private $checkreturn = false;

    /**
     * Is the symfony cli debug mode set? (true by default)
     * @var boolean
     */
    private $debug = true;
    
    /**
     * sets the symfony console command to execute
     * @param string $command
     */
    public function setCommand($command)
    {
        $this->command = $command;
    }

    /**
     * return the symfony console command to execute
     * @return String
     */
    public function getCommand()
    {
        return $this->command;
    }

    /**
     * sets the path to symfony console application
     * @param string $console
     */
    public function setConsole($console)
    {
        $this->console = $console;
    }

    /**
     * returns the path to symfony console application
     * @return string
     */
    public function getConsole()
    {
        return $this->console;
    }

    /**
     * Set the name of the property to store the application output in
     * @param $property
     * @return void
     */
    public function setPropertyName($property)
    {
        $this->propertyName = $property;
    }

    /**
     * Whether to check the return code.
     *
     * @param boolean $checkreturn If the return code shall be checked
     *
     * @return void
     */
    public function setCheckreturn($checkreturn)
    {
        $this->checkreturn = (bool) $checkreturn;
    }

    
    /**
     * Whether to set the symfony cli debug mode
     *
     * @param boolean $debug If the symfony cli debug mode is set
     *
     * @return void
     */
    public function setDebug($debug)
    {
        $this->debug = (bool) $debug;
    }

    /**
     * Get if the symfony cli debug mode is set
     * @return boolean
     */
    public function getDebug()
    {
        return $this->debug;
    }

    /**
     * appends an arg tag to the arguments stack
     *
     * @return Arg Argument object
     */

    public function createArg()
    {
        $num = array_push($this->args, new Arg());

        return $this->args[$num - 1];
    }

    /**
     * return the argumments passed to this task
     * @return array of Arg()
     */
    public function getArgs()
    {
        return $this->args;
    }

    /**
     * Check if the no-debug option was added via args
     * @return boolean
     */
    private function isNoDebugArgPresent()
    {
        foreach($this->args as $arg) {
            if ($arg->getName() == "no-debug") {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Gets the command string to be executed
     * @return string
     */
    public function getCmdString()
    {
        // Add no-debug arg if it isn't already present
        if (!$this->debug && !$this->isNoDebugArgPresent()) {
            $this->createArg()->setName("no-debug");
        }
        $cmd = array(
            Commandline::quoteArgument($this->console),
            $this->command,
            implode(' ', $this->args)
        );
        $cmd = implode(' ', $cmd);

        return $cmd;
    }

    /**
     * executes the synfony console application
     */
    public function main()
    {
        $cmd = $this->getCmdString();

        $this->log("executing $cmd");
        $return = null;
        $output = array();
        exec($cmd, $output, $return);

        $lines = implode("\r\n", $output);

        $this->log($lines, Project::MSG_INFO);

        if ($this->propertyName != null) {
            $this->project->setProperty($this->propertyName, $lines);
        }

        if ($return != 0 && $this->checkreturn) {
            $this->log('Task exited with code: ' . $return, Project::MSG_ERR);
            throw new BuildException("SymfonyConsole execution failed");
        }
    }
}
<?php

/*
 *  $Id: 04cb8e6079c1dd642f942085dafad99da004190d $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once "phing/Task.php";

/**
 * Generates symlinks based on a target / link combination.
 * Can also symlink contents of a directory, individually
 *
 * Single target symlink example:
 * <code>
 *     <symlink target="/some/shared/file" link="${project.basedir}/htdocs/my_file" />
 * </code>
 *
 * Symlink entire contents of directory
 *
 * This will go through the contents of "/my/shared/library/*"
 * and create a symlink for each entry into ${project.basedir}/library/
 * <code>
 *     <symlink link="${project.basedir}/library">
 *         <fileset dir="/my/shared/library">
 *             <include name="*" />
 *         </fileset>
 *     </symlink>
 * </code>
 *
 * @author Andrei Serdeliuc <andrei@serdeliuc.ro>
 * @extends Task
 * @version $ID$
 * @package phing.tasks.ext
 */
class SymlinkTask extends Task
{
    /**
     * What we're symlinking from
     *
     * (default value: null)
     *
     * @var string
     */
    private $_target = null;

    /**
     * Symlink location
     *
     * (default value: null)
     *
     * @var string
     */
    private $_link = null;

    /**
     * Collection of filesets
     * Used when linking contents of a directory
     *
     * (default value: array())
     *
     * @var array
     */
    private $_filesets = array();

    /**
     * Whether to override the symlink if it exists but points
     * to a different location
     *
     * (default value: false)
     *
     * @var boolean
     */
    private $_overwrite = false;

    /**
     * Whether to create relative symlinks
     *
     * @var boolean
     */
    private $relative = false;

    /**
     * setter for _target
     *
     * @param  string $target
     * @return void
     */
    public function setTarget($target)
    {
        $this->_target = $target;
    }

    /**
     * setter for _link
     *
     * @param  string $link
     * @return void
     */
    public function setLink($link)
    {
        $this->_link = $link;
    }

    /**
     * creator for _filesets
     *
     * @return FileSet
     */
    public function createFileset()
    {
        $num = array_push($this->_filesets, new FileSet());

        return $this->_filesets[$num - 1];
    }

    /**
     * setter for _overwrite
     *
     * @param  boolean $overwrite
     * @return void
     */
    public function setOverwrite($overwrite)
    {
        $this->_overwrite = $overwrite;
    }

    /**
     * @param boolean $relative
     */
    public function setRelative($relative)
    {
        $this->relative = $relative;
    }

    /**
     * getter for _target
     *
     * @throws BuildException
     * @return string
     */
    public function getTarget()
    {
        if ($this->_target === null) {
            throw new BuildException('Target not set');
        }

        return $this->_target;
    }

    /**
     * getter for _link
     *
     * @throws BuildException
     * @return string
     */
    public function getLink()
    {
        if ($this->_link === null) {
            throw new BuildException('Link not set');
        }

        return $this->_link;
    }

    /**
     * getter for _filesets
     *
     * @return array
     */
    public function getFilesets()
    {
        return $this->_filesets;
    }

    /**
     * getter for _overwrite
     *
     * @return boolean
     */
    public function getOverwrite()
    {
        return $this->_overwrite;
    }

    /**
     * @return boolean
     */
    public function isRelative()
    {
        return $this->relative;
    }

    /**
     * Generates an array of directories / files to be linked
     * If _filesets is empty, returns getTarget()
     *
     * @throws BuildException
     * @return array|string
     */
    protected function getMap()
    {
        $fileSets = $this->getFilesets();

        // No filesets set
        // We're assuming single file / directory
        if (empty($fileSets)) {
            return $this->getTarget();
        }

        $targets = array();

        foreach ($fileSets as $fs) {
            if (!($fs instanceof FileSet)) {
                continue;
            }

            // We need a directory to store the links
            if (!is_dir($this->getLink())) {
                throw new BuildException('Link must be an existing directory when using fileset');
            }

            if ($this->isRelative()) {
                $fromDir = $fs->getDir($this->getProject())->getPath();
            } else {
                $fromDir = $fs->getDir($this->getProject())->getAbsolutePath();
            }

            if (!is_dir($fromDir)) {
                $this->log('Directory doesn\'t exist: ' . $fromDir, Project::MSG_WARN);
                continue;
            }

            $fsTargets = array();

            $ds = $fs->getDirectoryScanner($this->getProject());

            $fsTargets = array_merge(
                $fsTargets,
                $ds->getIncludedDirectories(),
                $ds->getIncludedFiles()
            );

            // Add each target to the map
            foreach ($fsTargets as $target) {
                if (!empty($target)) {
                    $targets[$target] = $fromDir . DIRECTORY_SEPARATOR . $target;
                }
            }
        }

        return $targets;
    }

    /**
     * Main entry point for task
     *
     * @return bool
     */
    public function main()
    {
        $map = $this->getMap();

        // Single file symlink
        if (is_string($map)) {
            return $this->symlink($map, $this->getLink());
        }

        // Multiple symlinks
        foreach ($map as $name => $targetPath) {
            $this->symlink($targetPath, $this->getLink() . DIRECTORY_SEPARATOR . $name);
        }

        return true;
    }

    /**
     * Create the actual link
     *
     * @param  string $target
     * @param  string $link
     * @return bool
     */
    protected function symlink($target, $link)
    {
        $fs = FileSystem::getFileSystem();

        if (is_link($link) && @readlink($link) == $target) {
            $this->log('Link exists: ' . $link, Project::MSG_INFO);

            return true;
        }

        if (file_exists($link) || is_link($link)) {
            if (!$this->getOverwrite()) {
                $this->log('Not overwriting existing link ' . $link, Project::MSG_ERR);

                return false;
            }

            if (is_link($link) || is_file($link)) {
                $fs->unlink($link);
                $this->log('Link removed: ' . $link, Project::MSG_INFO);
            } else {
                $fs->rmdir($link, true);
                $this->log('Directory removed: ' . $link, Project::MSG_INFO);
            }
        }

        $this->log('Linking: ' . $target . ' to ' . $link, Project::MSG_INFO);

        return $fs->symlink($target, $link);
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/MatchingTask.php';
include_once 'phing/util/SourceFileScanner.php';
include_once 'phing/mappers/MergeMapper.php';
include_once 'phing/util/StringHelper.php';

/**
 * Creates a tar archive using PEAR Archive_Tar.
 *
 * @author    Hans Lellelid <hans@xmpl.org> (Phing)
 * @author    Stefano Mazzocchi <stefano@apache.org> (Ant)
 * @author    Stefan Bodewig <stefan.bodewig@epost.de> (Ant)
 * @author    Magesh Umasankar
 *
 * @package   phing.tasks.ext
 */
class TarTask extends MatchingTask
{

    const TAR_NAMELEN = 100;

    const WARN = "warn";
    const FAIL = "fail";
    const OMIT = "omit";

    private $tarFile;
    private $baseDir;
    private $includeEmpty = true; // Whether to include empty dirs in the TAR

    private $longFileMode = "warn";

    private $filesets = array();
    private $fileSetFiles = array();

    /**
     * Indicates whether the user has been warned about long files already.
     */
    private $longWarningGiven = false;

    /**
     * Compression mode.  Available options "gzip", "bzip2", "none" (null).
     */
    private $compression = null;

    /**
     * File path prefix in the tar archive
     *
     * @var string
     */
    private $prefix = null;

    /**
     * Ensures that PEAR lib exists.
     */
    public function init()
    {
        include_once 'Archive/Tar.php';
        if (!class_exists('Archive_Tar')) {
            throw new BuildException("You must have installed the PEAR Archive_Tar class in order to use TarTask.");
        }
    }

    /**
     * Add a new fileset
     * @return FileSet
     */
    public function createTarFileSet()
    {
        $this->fileset = new TarFileSet();
        $this->filesets[] = $this->fileset;

        return $this->fileset;
    }

    /**
     * Add a new fileset.  Alias to createTarFileSet() for backwards compatibility.
     * @return FileSet
     * @see createTarFileSet()
     */
    public function createFileSet()
    {
        $this->fileset = new TarFileSet();
        $this->filesets[] = $this->fileset;

        return $this->fileset;
    }

    /**
     * Set is the name/location of where to create the tar file.
     * @param PhingFile $destFile The output of the tar
     */
    public function setDestFile(PhingFile $destFile)
    {
        $this->tarFile = $destFile;
    }

    /**
     * This is the base directory to look in for things to tar.
     * @param PhingFile $baseDir
     */
    public function setBasedir(PhingFile $baseDir)
    {
        $this->baseDir = $baseDir;
    }

    /**
     * Set the include empty dirs flag.
     *
     * @param  boolean $bool Flag if empty dirs should be tarred too
     *
     * @return void
     */
    public function setIncludeEmptyDirs($bool)
    {
        $this->includeEmpty = (boolean) $bool;
    }

    /**
     * Set how to handle long files, those with a path&gt;100 chars.
     * Optional, default=warn.
     * <p>
     * Allowable values are
     * <ul>
     * <li>  truncate - paths are truncated to the maximum length
     * <li>  fail - paths greater than the maximim cause a build exception
     * <li>  warn - paths greater than the maximum cause a warning and GNU is used
     * <li>  gnu - GNU extensions are used for any paths greater than the maximum.
     * <li>  omit - paths greater than the maximum are omitted from the archive
     * </ul>
     * @param $mode
     */
    public function setLongfile($mode)
    {
        $this->longFileMode = $mode;
    }

    /**
     * Set compression method.
     * Allowable values are
     * <ul>
     * <li>  none - no compression
     * <li>  gzip - Gzip compression
     * <li>  bzip2 - Bzip2 compression
     * </ul>
     * @param string $mode
     */
    public function setCompression($mode)
    {
        switch ($mode) {
            case "gzip":
                $this->compression = "gz";
                break;
            case "bzip2":
                $this->compression = "bz2";
                break;
            case "none":
                $this->compression = null;
                break;
            default:
                $this->log("Ignoring unknown compression mode: " . $mode, Project::MSG_WARN);
                $this->compression = null;
        }
    }

    /**
     * Sets the file path prefix for file in the tar file.
     *
     * @param string $prefix Prefix
     *
     * @return void
     */
    public function setPrefix($prefix)
    {
        $this->prefix = $prefix;
    }

    /**
     * do the work
     * @throws BuildException
     */
    public function main()
    {
        if ($this->tarFile === null) {
            throw new BuildException("tarfile attribute must be set!", $this->getLocation());
        }

        if ($this->tarFile->exists() && $this->tarFile->isDirectory()) {
            throw new BuildException("tarfile is a directory!", $this->getLocation());
        }

        if ($this->tarFile->exists() && !$this->tarFile->canWrite()) {
            throw new BuildException("Can not write to the specified tarfile!", $this->getLocation());
        }

        // shouldn't need to clone, since the entries in filesets
        // themselves won't be modified -- only elements will be added
        $savedFileSets = $this->filesets;

        try {
            if ($this->baseDir !== null) {
                if (!$this->baseDir->exists()) {
                    throw new BuildException("basedir '" . (string) $this->baseDir . "' does not exist!", $this->getLocation(
                        ));
                }
                if (empty($this->filesets)) { // if there weren't any explicit filesets specivied, then
                    // create a default, all-inclusive fileset using the specified basedir.
                    $mainFileSet = new TarFileSet($this->fileset);
                    $mainFileSet->setDir($this->baseDir);
                    $this->filesets[] = $mainFileSet;
                }
            }

            if (empty($this->filesets)) {
                throw new BuildException("You must supply either a basedir "
                    . "attribute or some nested filesets.",
                    $this->getLocation());
            }

            // check if tar is out of date with respect to each fileset
            if ($this->tarFile->exists() && $this->isArchiveUpToDate()) {
                $this->log("Nothing to do: " . $this->tarFile->__toString() . " is up to date.", Project::MSG_INFO);
                return;
            }

            $this->log("Building tar: " . $this->tarFile->__toString(), Project::MSG_INFO);

            $tar = new Archive_Tar($this->tarFile->getAbsolutePath(), $this->compression);
            $pear = new PEAR();

            if ($pear->isError($tar->error_object)) {
                throw new BuildException($tar->error_object->getMessage());
            }

            foreach ($this->filesets as $fs) {
                $files = $fs->getFiles($this->project, $this->includeEmpty);
                if (count($files) > 1 && strlen($fs->getFullpath()) > 0) {
                    throw new BuildException("fullpath attribute may only "
                        . "be specified for "
                        . "filesets that specify a "
                        . "single file.");
                }
                $fsBasedir = $fs->getDir($this->project);
                $filesToTar = array();
                for ($i = 0, $fcount = count($files); $i < $fcount; $i++) {
                    $f = new PhingFile($fsBasedir, $files[$i]);
                    $filesToTar[] = $f->getAbsolutePath();
                    $this->log("Adding file " . $f->getPath() . " to archive.", Project::MSG_VERBOSE);
                }
                $tar->addModify($filesToTar, $this->prefix, $fsBasedir->getAbsolutePath());

                if ($pear->isError($tar->error_object)) {
                    throw new BuildException($tar->error_object->getMessage());
                }
            }


        } catch (IOException $ioe) {
            $msg = "Problem creating TAR: " . $ioe->getMessage();
            $this->filesets = $savedFileSets;
            throw new BuildException($msg, $ioe, $this->getLocation());
        }

        $this->filesets = $savedFileSets;
    }

    /**
     * @param  array     $files array of filenames
     * @param  PhingFile $dir
     *
     * @return boolean
     */
    protected function areFilesUpToDate($files, $dir)
    {
        $sfs = new SourceFileScanner($this);
        $mm = new MergeMapper();
        $mm->setTo($this->tarFile->getAbsolutePath());

        return count($sfs->restrict($files, $dir, null, $mm)) == 0;
    }

    /**
     * @return array
     * @throws BuildException
     */
    private function isArchiveUpToDate()
    {
        foreach ($this->filesets as $fs) {
            $files = $fs->getFiles($this->project, $this->includeEmpty);
            if (!$this->areFilesUpToDate($files, $fs->getDir($this->project))) {
                return false;
            }
            for ($i = 0, $fcount = count($files); $i < $fcount; $i++) {
                if ($this->tarFile->equals(new PhingFile($fs->getDir($this->project), $files[$i]))) {
                    throw new BuildException("A tar file cannot include itself", $this->getLocation());
                }
            }
        }
        return true;
    }
}


/**
 * This is a FileSet with the option to specify permissions.
 *
 * Permissions are currently not implemented by PEAR Archive_Tar,
 * but hopefully they will be in the future.
 *
 * @package   phing.tasks.ext
 */
class TarFileSet extends FileSet
{

    private $files = null;

    private $mode = 0100644;

    private $userName = "";
    private $groupName = "";
    private $prefix = "";
    private $fullpath = "";
    private $preserveLeadingSlashes = false;

    /**
     * Get a list of files and directories specified in the fileset.
     *
     * @param Project $p
     * @param bool $includeEmpty
     *
     * @throws BuildException
     *
     * @return array a list of file and directory names, relative to
     *               the baseDir for the project.
     */
    public function getFiles(Project $p, $includeEmpty = true)
    {

        if ($this->files === null) {

            $ds = $this->getDirectoryScanner($p);
            $this->files = $ds->getIncludedFiles();

            if ($includeEmpty) {

                // first any empty directories that will not be implicitly added by any of the files
                $implicitDirs = array();
                foreach ($this->files as $file) {
                    $implicitDirs[] = dirname($file);
                }

                $incDirs = $ds->getIncludedDirectories();

                // we'll need to add to that list of implicit dirs any directories
                // that contain other *directories* (and not files), since otherwise
                // we get duplicate directories in the resulting tar
                foreach ($incDirs as $dir) {
                    foreach ($incDirs as $dircheck) {
                        if (!empty($dir) && $dir == dirname($dircheck)) {
                            $implicitDirs[] = $dir;
                        }
                    }
                }

                $implicitDirs = array_unique($implicitDirs);

                // Now add any empty dirs (dirs not covered by the implicit dirs)
                // to the files array.

                foreach ($incDirs as $dir) { // we cannot simply use array_diff() since we want to disregard empty/. dirs
                    if ($dir != "" && $dir != "." && !in_array($dir, $implicitDirs)) {
                        // it's an empty dir, so we'll add it.
                        $this->files[] = $dir;
                    }
                }
            } // if $includeEmpty

        } // if ($this->files===null)

        return $this->files;
    }

    /**
     * A 3 digit octal string, specify the user, group and
     * other modes in the standard Unix fashion;
     * optional, default=0644
     * @param string $octalString
     */
    public function setMode($octalString)
    {
        $octal = (int) $octalString;
        $this->mode = 0100000 | $octal;
    }

    /**
     * @return int
     */
    public function getMode()
    {
        return $this->mode;
    }

    /**
     * The username for the tar entry
     * This is not the same as the UID, which is
     * not currently set by the task.
     * @param $userName
     */
    public function setUserName($userName)
    {
        $this->userName = $userName;
    }

    /**
     * @return string
     */
    public function getUserName()
    {
        return $this->userName;
    }

    /**
     * The groupname for the tar entry; optional, default=""
     * This is not the same as the GID, which is
     * not currently set by the task.
     * @param $groupName
     */
    public function setGroup($groupName)
    {
        $this->groupName = $groupName;
    }

    /**
     * @return string
     */
    public function getGroup()
    {
        return $this->groupName;
    }

    /**
     * If the prefix attribute is set, all files in the fileset
     * are prefixed with that path in the archive.
     * optional.
     * @param bool $prefix
     */
    public function setPrefix($prefix)
    {
        $this->prefix = $prefix;
    }

    /**
     * @return string
     */
    public function getPrefix()
    {
        return $this->prefix;
    }

    /**
     * If the fullpath attribute is set, the file in the fileset
     * is written with that path in the archive. The prefix attribute,
     * if specified, is ignored. It is an error to have more than one file specified in
     * such a fileset.
     * @param $fullpath
     */
    public function setFullpath($fullpath)
    {
        $this->fullpath = $fullpath;
    }

    /**
     * @return string
     */
    public function getFullpath()
    {
        return $this->fullpath;
    }

    /**
     * Flag to indicates whether leading `/'s` should
     * be preserved in the file names.
     * Optional, default is <code>false</code>.
     *
     * @param bool $b
     *
     * @return void
     */
    public function setPreserveLeadingSlashes($b)
    {
        $this->preserveLeadingSlashes = (boolean) $b;
    }

    /**
     * @return bool
     */
    public function getPreserveLeadingSlashes()
    {
        return $this->preserveLeadingSlashes;
    }
}
<?php
/*
 *  $Id: 3b63d08533321a87618f87c564bde9d609b91f36 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/FailTask.php';

/**
 * Exits the active build, giving an additional message
 * if available.
 *
 * @author    Hans Lellelid <hans@xmpl.org> (Phing)
 * @author    Nico Seessle <nico@seessle.de> (Ant)
 * @version   $Id: 3b63d08533321a87618f87c564bde9d609b91f36 $
 * @package   phing.tasks.system
 */
class ThrowTask extends FailTask
{
    /**
     * @var Reference
     */
    private $reference = null;

    /**
     * @throws BuildException
     */
    public function main()
    {
        $reffed = $this->reference !== null ? $this->reference->getReferencedObject($this->getProject()) : null;

        if ($reffed !== null && $reffed instanceof BuildException) {
            throw $reffed;
        }

        parent::main();
    }

    /**
     * @param Reference $ref
     *
     * @return void
     */
    public function setRefid(Reference $ref)
    {
        $this->reference = $ref;
    }

    /**
     * @return Reference
     */
    public function getRefid()
    {
        return $this->reference;
    }
}
<?php
/*
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/ExtractBaseTask.php';

/**
 * Extracts one or several tar archives using PEAR Archive_Tar
 *
 * @author    Joakim Bodin <joakim.bodin+phing@gmail.com>
 * @version   $Id: a7c20154729a165ba27135dfc3db76a96a11908b $
 * @package   phing.tasks.ext
 * @since     2.2.0
 */
class UntarTask extends ExtractBaseTask
{

    /**
     * @var bool
     */
    private $preservePermissions = false;

    /**
     * @param bool $preservePermissions
     */
    public function setPreservePermissions($preservePermissions)
    {
        $this->preservePermissions = $preservePermissions;
    }

    /**
     * Ensures that PEAR lib exists.
     */
    public function init()
    {
        include_once 'Archive/Tar.php';
        if (!class_exists('Archive_Tar')) {
            throw new BuildException("You must have installed the PEAR Archive_Tar class in order to use UntarTask.");
        }
    }

    /**
     * @param PhingFile $tarfile
     * @return mixed|void
     * @throws BuildException
     */
    protected function extractArchive(PhingFile $tarfile)
    {
        $this->log(
            "Extracting tar file: " . $tarfile->__toString() . ' to ' . $this->todir->__toString(),
            Project::MSG_INFO
        );

        try {
            $tar = $this->initTar($tarfile);
            if (!$tar->extractModify($this->todir->getAbsolutePath(), $this->removepath, $this->preservePermissions)) {
                throw new BuildException('Failed to extract tar file: ' . $tarfile->getAbsolutePath() . '. Error: ' . $tar->error_object->getMessage());
            }
        } catch (IOException $ioe) {
            $msg = "Could not extract tar file: " . $ioe->getMessage();
            throw new BuildException($msg, $ioe, $this->getLocation());
        }
    }

    /**
     * @param PhingFile $tarfile
     * @return array|int
     */
    protected function listArchiveContent(PhingFile $tarfile)
    {
        $tar = $this->initTar($tarfile);

        return $tar->listContent();
    }

    /**
     * Init a Archive_Tar class with correct compression for the given file.
     *
     * @param  PhingFile   $tarfile
     * @return Archive_Tar the tar class instance
     */
    private function initTar(PhingFile $tarfile)
    {
        $compression = null;
        $tarfileName = $tarfile->getName();
        $mode = strtolower(substr($tarfileName, strrpos($tarfileName, '.')));

        $compressions = array(
            'gz' => array('.gz', '.tgz',),
            'bz2' => array('.bz2',),
        );
        foreach ($compressions as $algo => $ext) {
            if (array_search($mode, $ext) !== false) {
                $compression = $algo;
                break;
            }
        }

        return new Archive_Tar($tarfile->getAbsolutePath(), $compression);
    }
}
<?php
/*
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/ExtractBaseTask.php';
require_once 'phing/system/io/FileSystem.php';

/**
 * Extracts one or several zip archives using ZipArchive class.
 *
 * @author  Joakim Bodin <joakim.bodin+phing@gmail.com>
 * @author  George Miroshnikov <laggy.luke@gmail.com>
 * @version $Id: 5cd1dcc3efd193ed2c40aa7ca2b39cfbb5accf80 $
 * @package phing.tasks.ext
 */
class UnzipTask extends ExtractBaseTask
{
    /**
     * Extract archive content into $this->todir directory
     * @param PhingFile Zip file to extract
     * @return boolean
     */
    protected function extractArchive(PhingFile $zipfile)
    {
        $this->log(
            "Extracting zip: " . $zipfile->__toString() . ' to ' . $this->todir->__toString(),
            Project::MSG_INFO
        );

        $zip = new ZipArchive();

        $result = $zip->open($zipfile->getAbsolutePath());
        if (!$result) {
            $this->log("Unable to open zipfile " . $zipfile->__toString(), Project::MSG_ERR);

            return false;
        }

        $result = $zip->extractTo($this->todir->getAbsolutePath());
        if (!$result) {
            $this->log("Unable to extract zipfile " . $zipfile->__toString(), Project::MSG_ERR);

            return false;
        }

        return true;
    }

    /**
     * List archive content
     * @param PhingFile Zip file to list content
     * @return array List of files inside $zipfile
     */
    protected function listArchiveContent(PhingFile $zipfile)
    {
        $zip = new ZipArchive();
        $zip->open($zipfile->getAbsolutePath());

        $content = array();
        for ($i = 0; $i < $zip->numFiles; $i++) {
            $content[] = array(
                'filename' => $zip->getNameIndex($i)
            );
        }

        return $content;
    }
}
<?php
/*
 * $Id: 3e7c572ae9bdb739905b54e831e3985194d65071 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */
require_once 'phing/Task.php';

/**
 * VersionTask
 *
 * Increments a three-part version number from a given file
 * and writes it back to the file.
 * Incrementing is based on given releasetype, which can be one
 * of Major, Minor and Bugfix.
 * Resulting version number is also published under supplied property.
 *
 * @author      Mike Wittje <mw@mike.wittje.de>
 * @version     $Id: 3e7c572ae9bdb739905b54e831e3985194d65071 $ $Rev $Id: 3e7c572ae9bdb739905b54e831e3985194d65071 $ $Author$
 * @package     phing.tasks.ext
 */
class VersionTask extends Task
{
    /**
     * Property for Releasetype
     * @var string $releasetype
     */
    private $releasetype;

    /**
     * Property for File
     * @var PhingFile file
     */
    private $file;

    /**
     * Property to be set
     * @var string $property
     */
    private $property;

    /* Allowed Releastypes */
    const RELEASETYPE_MAJOR = 'MAJOR';
    const RELEASETYPE_MINOR = 'MINOR';
    const RELEASETYPE_BUGFIX = 'BUGFIX';

    /**
     * Set Property for Releasetype (Minor, Major, Bugfix)
     * @param string $releasetype
     */
    public function setReleasetype($releasetype)
    {
        $this->releasetype = strtoupper($releasetype);
    }

    /**
     * Set Property for File containing versioninformation
     * @param PhingFile $file
     */
    public function setFile($file)
    {
        $this->file = $file;
    }

    /**
     * Set name of property to be set
     * @param $property
     * @return void
     */
    public function setProperty($property)
    {
        $this->property = $property;
    }

    /**
     * Main-Method for the Task
     *
     * @return void
     * @throws BuildException
     */
    public function main()
    {
        // check supplied attributes
        $this->checkReleasetype();
        $this->checkFile();
        $this->checkProperty();

        // read file
        $filecontent = trim(file_get_contents($this->file));

        // get new version
        $newVersion = $this->getVersion($filecontent);

        // write new Version to file
        file_put_contents($this->file, $newVersion . "\n");

        // publish new version number as property
        $this->project->setProperty($this->property, $newVersion);

    }

    /**
     * Returns new version number corresponding to Release type
     *
     * @param  string $filecontent
     * @return string
     */
    private function getVersion($filecontent)
    {
        // init
        $newVersion = '';

        // Extract version
        list($major, $minor, $bugfix) = explode(".", $filecontent);

        // Return new version number
        switch ($this->releasetype) {
            case self::RELEASETYPE_MAJOR:
                $newVersion = sprintf(
                    "%d.%d.%d",
                    ++$major,
                    0,
                    0
                );
                break;

            case self::RELEASETYPE_MINOR:
                $newVersion = sprintf(
                    "%d.%d.%d",
                    $major,
                    ++$minor,
                    0
                );
                break;

            case self::RELEASETYPE_BUGFIX:
                $newVersion = sprintf(
                    "%d.%d.%d",
                    $major,
                    $minor,
                    ++$bugfix
                );
                break;
        }

        return $newVersion;
    }

    /**
     * checks releasetype attribute
     * @return void
     * @throws BuildException
     */
    private function checkReleasetype()
    {
        // check Releasetype
        if (is_null($this->releasetype)) {
            throw new BuildException('releasetype attribute is required', $this->location);
        }
        // known releasetypes
        $releaseTypes = array(
            self::RELEASETYPE_MAJOR,
            self::RELEASETYPE_MINOR,
            self::RELEASETYPE_BUGFIX
        );

        if (!in_array($this->releasetype, $releaseTypes)) {
            throw new BuildException(sprintf(
                'Unknown Releasetype %s..Must be one of Major, Minor or Bugfix',
                $this->releasetype
            ), $this->location);
        }
    }

    /**
     * checks file attribute
     * @return void
     * @throws BuildException
     */
    private function checkFile()
    {
        // check File
        if ($this->file === null ||
            strlen($this->file) == 0
        ) {
            throw new BuildException('You must specify a file containing the version number', $this->location);
        }

        $content = file_get_contents($this->file);
        if (strlen($content) == 0) {
            throw new BuildException(sprintf('Supplied file %s is empty', $this->file), $this->location);
        }

        // check for three-part number
        $split = explode('.', $content);
        if (count($split) !== 3) {
            throw new BuildException('Unknown version number format', $this->location);
        }

    }

    /**
     * checks property attribute
     * @return void
     * @throws BuildException
     */
    private function checkProperty()
    {
        if (is_null($this->property) ||
            strlen($this->property) === 0
        ) {
            throw new BuildException('Property for publishing version number is not set', $this->location);
        }
    }
}
<?php
/*
 *  $Id: f156eaee85b94f50511ae48d36758ea1bc57ed63 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Publish Wiki document using Wiki API.
 *
 * @author  Piotr Lewandowski <piotr@cassis.pl>
 * @package phing.tasks.ext
 */
class WikiPublishTask extends Task
{
    /**
     * Wiki API url
     *
     * @var string
     */
    private $apiUrl;
    /**
     * Wiki API user name
     *
     * @var string
     */
    private $apiUser;
    /**
     * Wiki API password
     *
     * @var string
     */
    private $apiPassword;
    /**
     * Wiki document Id. Document can be identified by title instead.
     *
     * @var int
     */
    private $id;
    /**
     * Wiki document title
     *
     * @var string
     */
    private $title;
    /**
     * Wiki document content
     *
     * @var string
     */
    private $content;
    /**
     * Publishing mode (append, prepend, overwrite).
     *
     * @var string
     */
    private $mode = 'append';
    /**
     * Publish modes map
     * @var array
     */
    private $modeMap = array(
        'overwrite' => 'text',
        'append' => 'appendtext',
        'prepend' => 'prependtext',
    );
    /**
     * Curl handler
     *
     * @var resource
     */
    private $curl;
    /**
     * Wiki api edit token
     *
     * @var string
     */
    private $apiEditToken;
    /**
     * Temporary cookies file
     *
     * @var string
     */
    private $cookiesFile;

    /**
     * @param string $apiPassword
     */
    public function setApiPassword($apiPassword)
    {
        $this->apiPassword = $apiPassword;
    }

    /**
     * @return string
     */
    public function getApiPassword()
    {
        return $this->apiPassword;
    }

    /**
     * @param string $apiUrl
     */
    public function setApiUrl($apiUrl)
    {
        $this->apiUrl = $apiUrl;
    }

    /**
     * @return string
     */
    public function getApiUrl()
    {
        return $this->apiUrl;
    }

    /**
     * @param string $apiUser
     */
    public function setApiUser($apiUser)
    {
        $this->apiUser = $apiUser;
    }

    /**
     * @return string
     */
    public function getApiUser()
    {
        return $this->apiUser;
    }

    /**
     * @param int $id
     */
    public function setId($id)
    {
        $this->id = $id;
    }

    /**
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @param string $mode
     * @throws BuildException
     */
    public function setMode($mode)
    {
        if (false === isset($this->modeMap[$mode])) {
            throw new BuildException('Mode is invalid (' . $mode . ', should be one of ' . implode(
                    ',',
                    array_keys($this->modeMap)
                ) . ')');
        }
        $this->mode = $mode;
    }

    /**
     * @return string
     */
    public function getMode()
    {
        return $this->mode;
    }

    /**
     * @param string $title
     */
    public function setTitle($title)
    {
        $this->title = $title;
    }

    /**
     * @return string
     */
    public function getTitle()
    {
        return $this->title;
    }

    /**
     * @param string $content
     */
    public function setContent($content)
    {
        $this->content = $content;
    }

    /**
     * @return string
     */
    public function getContent()
    {
        return $this->content;
    }

    /**
     * Prepare CURL object
     *
     * @throws BuildException
     */
    public function init()
    {
        $this->cookiesFile = tempnam(sys_get_temp_dir(), 'WikiPublish.' . uniqid() . '.cookies');

        $this->curl = curl_init();
        if (false === is_resource($this->curl)) {
            throw new BuildException('Curl init failed (' . $this->apiUrl . ')');
        }

        curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($this->curl, CURLOPT_COOKIEJAR, $this->cookiesFile);
        curl_setopt($this->curl, CURLOPT_COOKIEFILE, $this->cookiesFile);
    }

    /**
     * The main entry point method
     */
    public function main()
    {
        $this->validateAttributes();
        $this->callApiLogin();
        $this->callApiEdit();
    }

    /**
     * Close curl connection and clean up
     */
    public function __destruct()
    {
        if (null !== $this->curl && is_resource($this->curl)) {
            curl_close($this->curl);
        }
        if (null !== $this->cookiesFile && file_exists($this->cookiesFile)) {
            unlink($this->cookiesFile);
        }
    }

    /**
     * Validates attributes coming in from XML
     *
     * @throws BuildException
     */
    private function validateAttributes()
    {

        if (null === $this->apiUrl) {
            throw new BuildException('Wiki apiUrl is required');
        }

        if (null === $this->id && null === $this->title) {
            throw new BuildException('Wiki page id or title is required');
        }

    }

    /**
     * Call Wiki webapi login action
     *
     * @param string|null $token
     *
     * @throws BuildException
     */
    private function callApiLogin($token = null)
    {
        $postData = array('lgname' => $this->apiUser, 'lgpassword' => $this->apiPassword);
        if (null !== $token) {
            $postData['lgtoken'] = $token;
        }

        $result = $this->callApi('action=login', $postData);
        try {
            $this->checkApiResponseResult('login', $result);
        } catch (BuildException $e) {
            if (null !== $token) {
                throw $e;
            }
            // if token is required then call login again with token
            $this->checkApiResponseResult('login', $result, 'NeedToken');
            if (isset($result['login']) && isset($result['login']['token'])) {
                $this->callApiLogin($result['login']['token']);
            } else {
                throw $e;
            }
        }
    }

    /**
     * Call Wiki webapi edit action
     */
    private function callApiEdit()
    {
        $this->callApiTokens();
        $result = $this->callApi('action=edit&token=' . urlencode($this->apiEditToken), $this->getApiEditData());
        $this->checkApiResponseResult('edit', $result);
    }

    /**
     * Return prepared data for Wiki webapi edit action
     *
     * @return array
     */
    private function getApiEditData()
    {
        $result = array(
            'minor' => '',
        );
        if (null !== $this->title) {
            $result['title'] = $this->title;
        }
        if (null !== $this->id) {
            $result['pageid'] = $this->id;
        }
        $result[$this->modeMap[$this->mode]] = $this->content;

        return $result;
    }

    /**
     * Call Wiki webapi tokens action
     *
     * @throws BuildException
     */
    private function callApiTokens()
    {
        $result = $this->callApi('action=tokens&type=edit');
        if (false == isset($result['tokens']) || false == isset($result['tokens']['edittoken'])) {
            throw new BuildException('Wiki token not found');
        }

        $this->apiEditToken = $result['tokens']['edittoken'];
    }

    /**
     * Call Wiki webapi
     *
     * @param string     $queryString
     * @param array|null $postData
     *
     * @return array
     * @throws BuildException
     */
    protected function callApi($queryString, $postData = null)
    {
        $this->setPostData($postData);

        $url = $this->apiUrl . '?' . $queryString . '&format=php';

        curl_setopt($this->curl, CURLOPT_URL, $url);

        $response = curl_exec($this->curl);
        $responseCode = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);

        if (200 !== $responseCode) {
            throw new BuildException('Wiki webapi call failed (http response ' . $responseCode . ')');
        }

        $result = @unserialize($response);
        if (false === $result) {
            throw new BuildException('Couldn\'t unserialize Wiki webapi response');
        }

        return $result;
    }

    /**
     * Set POST data for curl call
     *
     * @param array|null $data
     */
    private function setPostData($data = null)
    {
        if (null === $data) {
            curl_setopt($this->curl, CURLOPT_POST, false);

            return;
        }
        $postData = '';
        foreach ($data as $key => $value) {
            $postData .= urlencode($key) . '=' . urlencode($value) . '&';
        }
        if ($postData != '') {
            curl_setopt($this->curl, CURLOPT_POST, true);
            curl_setopt($this->curl, CURLOPT_POSTFIELDS, substr($postData, 0, -1));
        }
    }

    /**
     * Validate Wiki webapi response
     *
     * @param string $action
     * @param array  $response
     * @param string $expect
     *
     * @throws BuildException
     */
    private function checkApiResponseResult($action, $response, $expect = 'Success')
    {
        if (isset($response['error'])) {
            throw new BuildException(
                'Wiki response error (action: ' . $action . ', error code: ' . $response['error']['code'] . ')');
        }
        if (false == isset($response[$action]) || false == isset($response[$action]['result'])) {
            throw new BuildException('Wiki response result not found (action: ' . $action . ')');
        }
        if ($response[$action]['result'] !== $expect) {
            throw new BuildException(
                'Unexpected Wiki response result ' . $response[$action]['result'] . ' (expected: ' . $expect . ')');
        }
    }
}
<?php
/*
 *  $Id: d9b238ee17a88afe34f9db53a4c944c92ac27278 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * A XML lint task. Checking syntax of one or more XML files against an XML Schema using the DOM extension.
 *
 * @author   Knut Urdalen <knut.urdalen@telio.no>
 * @version  $Id: d9b238ee17a88afe34f9db53a4c944c92ac27278 $
 * @package  phing.tasks.ext
 */
class XmlLintTask extends Task
{

    protected $file; // the source file (from xml attribute)
    protected $schema; // the schema file (from xml attribute)
    protected $filesets = array(); // all fileset objects assigned to this task
    protected $useRNG = false;

    protected $haltonfailure = true;

    /**
     * File to be performed syntax check on
     *
     * @param PhingFile $file
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * XML Schema Description file to validate against
     *
     * @param PhingFile $schema
     */
    public function setSchema(PhingFile $schema)
    {
        $this->schema = $schema;
    }

    /**
     * Use RNG instead of DTD schema validation
     *
     * @param bool $bool
     */
    public function setUseRNG($bool)
    {
        $this->useRNG = (boolean) $bool;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     *
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Sets the haltonfailure attribute
     *
     * @param bool $haltonfailure
     *
     * @return void
     */
    public function setHaltonfailure($haltonfailure)
    {
        $this->haltonfailure = (bool) $haltonfailure;
    }

    /**
     * Execute lint check against PhingFile or a FileSet.
     *
     * {@inheritdoc}
     *
     * @throws BuildException
     *
     * @return void
     */
    public function main()
    {
        if (isset($this->schema) && !file_exists($this->schema->getPath())) {
            throw new BuildException("Schema file not found: " . $this->schema->getPath());
        }
        if (!isset($this->file) and count($this->filesets) == 0) {
            throw new BuildException("Missing either a nested fileset or attribute 'file' set");
        }

        set_error_handler(array($this, 'errorHandler'));
        if ($this->file instanceof PhingFile) {
            $this->lint($this->file->getPath());
        } else { // process filesets
            $project = $this->getProject();
            foreach ($this->filesets as $fs) {
                $ds = $fs->getDirectoryScanner($project);
                $files = $ds->getIncludedFiles();
                $dir = $fs->getDir($this->project)->getPath();
                foreach ($files as $file) {
                    $this->lint($dir . DIRECTORY_SEPARATOR . $file);
                }
            }
        }
        restore_error_handler();
    }

    /**
     * @param $message
     *
     * @return void
     *
     * @throws BuildException
     */
    protected function logError($message)
    {
        if ($this->haltonfailure) {
            throw new BuildException($message);
        } else {
            $this->log($message, Project::MSG_ERR);
        }
    }

    /**
     * Performs validation
     *
     * @param  string $file
     *
     * @return void
     */
    protected function lint($file)
    {
        if (file_exists($file)) {
            if (is_readable($file)) {
                $dom = new DOMDocument();
                if ($dom->load($file) === false) {
                    $error = libxml_get_last_error();
                    $this->logError($file . ' is not well-formed (See messages above)');
                } else {
                    if (isset($this->schema)) {
                        if ($this->useRNG) {
                            if ($dom->relaxNGValidate($this->schema->getPath())) {
                                $this->log($file . ' validated with RNG grammar', Project::MSG_INFO);
                            } else {
                                $this->logError($file . ' fails to validate (See messages above)');
                            }
                        } else {
                            if ($dom->schemaValidate($this->schema->getPath())) {
                                $this->log($file . ' validated with schema', Project::MSG_INFO);
                            } else {
                                $this->logError($file . ' fails to validate (See messages above)');
                            }
                        }
                    } else {
                        $this->log(
                            $file . ' is well-formed (not validated due to missing schema specification)',
                            Project::MSG_INFO
                        );
                    }
                }
            } else {
                $this->logError('Permission denied to read file: ' . $file);
            }
        } else {
            $this->logError('File not found: ' . $file);
        }
    }

    /**
     * Local error handler to catch validation errors and log them through Phing
     *
     * @param int $level
     * @param string $message
     * @param string $file
     * @param int $line
     * @param mixed $context
     *
     * @return void
     */
    public function errorHandler($level, $message, $file, $line, $context)
    {
        $matches = array();
        preg_match('/^.*\(\): (.*)$/', $message, $matches);
        $this->log($matches[1], Project::MSG_ERR);
    }
}
<?php

/*
 *  $Id: d9741f5e9084e1c59c5dfeb32524d56fe052ecfc $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/tasks/system/PropertyTask.php';

/**
 * Task for setting properties from an XML file in buildfiles.
 *
 * @author    Jonathan Bond-Caron <jbondc@openmv.com>
 * @version   $Id: d9741f5e9084e1c59c5dfeb32524d56fe052ecfc $
 * @package   phing.tasks.ext
 * @since     2.4.0
 * @link      http://ant.apache.org/manual/CoreTasks/xmlproperty.html
 */
class XmlPropertyTask extends PropertyTask
{

    private $_keepRoot = true;
    private $_collapseAttr = false;
    private $_delimiter = ',';
    private $_required = false;

    /** Set a file to use as the source for properties.
     * @param $file
     */
    public function setFile($file)
    {
        if (is_string($file)) {
            $file = new PhingFile($file);
        }
        $this->file = $file;
    }

    /** Get the PhingFile that is being used as property source. */
    public function getFile()
    {
        return $this->file;
    }

    /**
     * Prefix to apply to properties loaded using <code>file</code>.
     * A "." is appended to the prefix if not specified.
     * @param  string $prefix prefix string
     * @return void
     * @since 2.0
     */
    public function setPrefix($prefix)
    {
        $this->prefix = $prefix;
        if (!StringHelper::endsWith(".", $prefix)) {
            $this->prefix .= ".";
        }
    }

    /**
     * @return string
     * @since 2.0
     */
    public function getPrefix()
    {
        return $this->prefix;
    }

    /**
     * Keep the xml root tag as the first value in the property name
     *
     * @param bool $yesNo
     */
    public function setKeepRoot($yesNo)
    {
        $this->_keepRoot = (bool) $yesNo;
    }

    /**
     * @return bool
     */
    public function getKeepRoot()
    {
        return $this->_keepRoot;
    }

    /**
     * Treat attributes as nested elements.
     *
     * @param bool $yesNo
     */
    public function setCollapseAttributes($yesNo)
    {
        $this->_collapseAttr = (bool) $yesNo;
    }

    /**
     * @return bool
     */
    public function getCollapseAttributes()
    {
        return $this->_collapseAttr;
    }

    /**
     * Delimiter for splitting multiple values.
     *
     * @param string $d
     */
    public function setDelimiter($d)
    {
        $this->_delimiter = $d;
    }

    /**
     * @return string
     */
    public function getDelimiter()
    {
        return $this->_delimiter;
    }

    /**
     * File required or not.
     *
     * @param string $d
     */
    public function setRequired($d)
    {
        $this->_required = $d;
    }

    /**
     * @return string
     */
    public function getRequired()
    {
        return $this->_required;
    }

    /**
     * set the property in the project to the value.
     * if the task was give a file or env attribute
     * here is where it is loaded
     */
    public function main()
    {

        if ($this->file === null) {
            throw new BuildException("You must specify file to load properties from", $this->getLocation());
        }

        $props = $this->loadFile($this->file);
        $this->addProperties($props);
    }

    /**
     * load properties from an XML file.
     * @param PhingFile $file
     * @throws BuildException
     * @return Properties
     */
    protected function loadFile(PhingFile $file)
    {
        $props = new Properties();
        $this->log("Loading " . $file->getAbsolutePath(), Project::MSG_INFO);
        try { // try to load file
            if ($file->exists()) {
                return $this->_getProperties($file);

            } else {
                if ($this->getRequired()) {
                    throw new BuildException("Could not load required properties file.", $ioe);
                } else {
                    $this->log(
                        "Unable to find property file: " . $file->getAbsolutePath() . "... skipped",
                        Project::MSG_WARN
                    );
                }
            }
        } catch (IOException $ioe) {
            throw new BuildException("Could not load properties from file.", $ioe);
        }
    }

    /**
     * Parses an XML file and returns properties
     *
     * @param string $filePath
     *
     * @throws IOException
     * @return Properties
     */
    protected function _getProperties($filePath)
    {

        // load() already made sure that file is readable
        // but we'll double check that when reading the file into
        // an array

        if (($lines = @file($filePath)) === false) {
            throw new IOException("Unable to parse contents of $filePath");
        }

        $prop = new Properties();

        $xml = simplexml_load_file($filePath);

        if ($xml === false) {
            throw new IOException("Unable to parse XML file $filePath");
        }

        $path = array();

        if ($this->_keepRoot) {
            $path[] = dom_import_simplexml($xml)->tagName;

            $prefix = implode('.', $path);

            if (!empty($prefix)) {
                $prefix .= '.';
            }

            // Check for attributes
            foreach ($xml->attributes() as $attribute => $val) {
                if ($this->_collapseAttr) {
                    $prop->setProperty($prefix . "$attribute", (string) $val);
                } else {
                    $prop->setProperty($prefix . "($attribute)", (string) $val);
                }
            }
        }

        $this->_addNode($xml, $path, $prop);

        return $prop;
    }

    /**
     * Adds an XML node
     *
     * @param SimpleXMLElement $node
     * @param array            $path Path to this node
     * @param Properties       $prop Properties will be added as they are found (by reference here)
     *
     * @return void
     */
    protected function _addNode($node, $path, $prop)
    {
        foreach ($node as $tag => $value) {

            $prefix = implode('.', $path);

            if (!empty($prefix) > 0) {
                $prefix .= '.';
            }

            // Check for attributes
            foreach ($value->attributes() as $attribute => $val) {
                if ($this->_collapseAttr) {
                    $prop->setProperty($prefix . "$tag.$attribute", (string) $val);
                } else {
                    $prop->setProperty($prefix . "$tag($attribute)", (string) $val);
                }
            }

            // Add tag
            if (count($value->children())) {
                $this->_addNode($value, array_merge($path, array($tag)), $prop);
            } else {
                $val = (string) $value;

                /* Check for * and ** on 'exclude' and 'include' tag / ant seems to do this? could use FileSet here
                if ($tag == 'exclude') {
                }*/

                // When property already exists, i.e. multiple xml tag
                // <project>
                //    <exclude>file/a.php</exclude>
                //    <exclude>file/a.php</exclude>
                // </project>
                //
                // Would be come project.exclude = file/a.php,file/a.php
                $p = empty($prefix) ? $tag : $prefix . $tag;
                $prop->append($p, (string) $val, $this->_delimiter);
            }
        }
    }
}
<?php
/*
 *  $Id: d1bcb58746c4f9bb6e63c43e61c9074f511ad456 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * ZendCodeAnalyzerTask analyze PHP source code using the ZendCodeAnalyzer included in Zend Studio 5.1
 *
 * Available warnings:
 * <b>zend-error</b> - %s(line %d): %s
 * <b>oneline-comment</b> - One-line comment ends with  tag.
 * <b>bool-assign</b> - Assignment seen where boolean expression is expected. Did you mean '==' instead of '='?
 * <b>bool-print</b> - Print statement used when boolean expression is expected.
 * <b>bool-array</b> - Array used when boolean expression is expected.
 * <b>bool-object</b> - Object used when boolean expression is expected.
 * <b>call-time-ref</b> - Call-time reference is deprecated. Define function as accepting parameter by reference instead.
 * <b>if-if-else</b> - In if-if-else construction else relates to the closest if. Use braces to make the code clearer.
 * <b>define-params</b> - define() requires two or three parameters.
 * <b>define-const</b> - First parameter for define() should be string. Maybe you forgot quotes?
 * <b>break-var</b> - Break/continue with variable is dangerous - break level can be out of scope.
 * <b>break-depth</b> - Break/continue with depth more than current nesting level.
 * <b>var-once</b> - Variable '%s' encountered only once. May be a typo?
 * <b>var-arg-unused</b> - Function argument '%s' is never used.
 * <b>var-global-unused</b> - Global variable '%s' is defined but never used.
 * <b>var-use-before-def</b> - Variable '%s' is used before it was assigned.
 * <b>var-use-before-def-global</b> - Global variable '%s' is used without being assigned. You are probably relying on register_globals feature of PHP. Note that this feature is off by default.
 * <b>var-no-global</b> - PHP global variable '%s' is used as local. Maybe you wanted to define '%s' as global?
 * <b>var-value-unused</b> - Value assigned to variable '%s' is never used
 * <b>var-ref-notmodified</b> - Function parameter '%s' is passed by reference but never modified. Consider passing by value.
 * <b>return-empty-val</b> - Function '%s' has both empty return and return with value.
 * <b>return-empty-used</b> - Function '%s' has empty return but return value is used.
 * <b>return-noref</b> - Function '%s' returns reference but the value is not assigned by reference. Maybe you meant '=&' instead of '='?
 * <b>return-end-used</b> - Control reaches the end of function '%s'(file %s, line %d) but return value is used.
 * <b>sprintf-miss-args</b> - Missing arguments for sprintf: format reqires %d arguments but %d are supplied.
 * <b>sprintf-extra-args</b> - Extra arguments for sprintf: format reqires %d arguments but %d are supplied.
 * <b>unreach-code</b> - Unreachable code in function '%s'.
 * <b>include-var</b> - include/require with user-accessible variable can be dangerous. Consider using constant instead.
 * <b>non-object</b> - Variable '%s' used as object, but has different type.
 * <b>bad-escape</b> - Bad escape sequence: \%c, did you mean \\%c?
 * <b>empty-cond</b> - Condition without a body
 * <b>expr-unused</b> - Expression result is never used
 *
 * @author   Knut Urdalen <knut.urdalen@gmail.com>
 * @version  $Id: d1bcb58746c4f9bb6e63c43e61c9074f511ad456 $
 * @package  phing.tasks.ext
 */
class ZendCodeAnalyzerTask extends Task
{
    protected $analyzerPath = ""; // Path to ZendCodeAnalyzer binary
    protected $file = ""; // the source file (from xml attribute)
    protected $filesets = array(); // all fileset objects assigned to this task
    protected $counter = 0;
    protected $disable = array();
    protected $enable = array();

    private $haltonwarning = false;

    /**
     * File to be analyzed
     *
     * @param PhingFile $file
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * Path to ZendCodeAnalyzer binary
     *
     * @param string $analyzerPath
     */
    public function setAnalyzerPath($analyzerPath)
    {
        $this->analyzerPath = $analyzerPath;
    }

    /**
     * Disable warning levels. Separate warning levels with ','
     *
     * @param string $disable
     */
    public function setDisable($disable)
    {
        $this->disable = explode(",", $disable);
    }

    /**
     * Enable warning levels. Separate warning levels with ','
     *
     * @param string $enable
     */
    public function setEnable($enable)
    {
        $this->enable = explode(",", $enable);
    }

    /**
     * Sets the haltonwarning flag
     * @param boolean $value
     */
    public function setHaltonwarning($value)
    {
        $this->haltonwarning = $value;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Analyze against PhingFile or a FileSet
     */
    public function main()
    {
        if (!isset($this->analyzerPath)) {
            throw new BuildException("Missing attribute 'analyzerPath'");
        }

        if (!isset($this->file) and count($this->filesets) == 0) {
            throw new BuildException("Missing either a nested fileset or attribute 'file' set");
        }

        if ($this->file instanceof PhingFile) {
            $this->analyze($this->file->getPath());
        } else { // process filesets
            $project = $this->getProject();

            foreach ($this->filesets as $fs) {
                $ds = $fs->getDirectoryScanner($project);
                $files = $ds->getIncludedFiles();
                $dir = $fs->getDir($this->project)->getPath();

                foreach ($files as $file) {
                    $this->analyze($dir . DIRECTORY_SEPARATOR . $file);
                }
            }
        }

        $this->log("Number of findings: " . $this->counter, Project::MSG_INFO);
    }

    /**
     * Analyze file
     *
     * @param  string $file
     * @throws BuildException
     * @return void
     */
    protected function analyze($file)
    {
        if (file_exists($file)) {
            if (is_readable($file)) {
                // Construct shell command
                $cmd = $this->analyzerPath . " ";

                foreach ($this->enable as $enable) { // Enable warning levels
                    $cmd .= " --enable $enable ";
                }

                foreach ($this->disable as $disable) { // Disable warning levels
                    $cmd .= " --disable $disable ";
                }

                $cmd .= "$file 2>&1";

                // Execute command
                $result = shell_exec($cmd);
                $result = explode("\n", $result);

                for ($i = 2, $size = count($result); $i < ($size - 1); $i++) {
                    $this->counter++;
                    $this->log($result[$i], Project::MSG_WARN);
                }

                $total = count($result) - 3;

                if ($total > 0 && $this->haltonwarning) {
                    throw new BuildException('zendcodeanalyzer detected ' . $total . ' warning' . ($total > 1 ? 's' : '') . ' in ' . $file);
                }
            } else {
                throw new BuildException('Permission denied: ' . $file);
            }
        } else {
            throw new BuildException('File not found: ' . $file);
        }
    }
}
<?php

/*
 *  $Id: aacbd9ea1b3f9e13178cea0f5ff625f921076a32 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/MatchingTask.php';
include_once 'phing/util/SourceFileScanner.php';
include_once 'phing/mappers/MergeMapper.php';
include_once 'phing/util/StringHelper.php';

/**
 * Encodes files using Zeng Guard Encoder
 *
 * @author    Petr Rybak <petr@rynawe.net>
 * @version   $Id: aacbd9ea1b3f9e13178cea0f5ff625f921076a32 $
 * @package   phing.tasks.ext.zendguard
 * @since     2.4.3
 */
class ZendGuardEncodeTask extends MatchingTask
{
    protected $filesets = array();
    protected $encodeCommand;

    /**
     * TASK PROPERTIES
     *
     * See http://static.zend.com/topics/Zend-Guard-User-Guidev5x.pdf
     * for more information on how to use ZendGuard
     *
     */
    /**
     * Permanently deletes (see warning below) the original source files specified in the
     * SourceInputPath and saves the encoded files in its place.
     * This option has no option parameter.
     * When this option is use, do not use the output_file parameter.
     *
     * Warning:
     * To avoid permanent loss of non-encoded scripts, make a backup. Deleted files
     * cannot be restored or recovered and will be permanently deleted with this option.
     * If you are unsure about deleting the source files, use the ––rename-source option
     * instead
     *
     * @var bool
     */
    protected $deleteSource = true;
    /**
     * Move the original source file to <input_file>.<renameSourceExt> and save the encoded file in its
     * place.
     *
     * If specified deleteSource will be automatically disabled.
     *
     * @var string
     */
    protected $renameSourceExt = null;
    /**
     * Turns short PHP tag (“<?” ) recognition either on or off.
     * On or off must be specified as an argument when using this option.
     * The default, when option is not used in the command-line, is - on
     *
     * @var bool
     */
    protected $shortTags = true;
    /**
     * Turn ASP tag (“<%” ) recognition on/off. (default: off). On or off must be specified
     * as an argument when using this option.
     * The default, when this option is not used in the command-line, is - off
     *
     * @var bool
     */
    protected $aspTags = false;
    /**
     *
     * Disables the PHP-compatible header that is added to the top of every encoded file
     * by default. Encoded files generated with this option will not display a meaningful
     * error when loaded by PHP that doesn't have the Zend Optimizer properly installed.
     * Using this option saves approximately 1.5KB for every encoded file. Do not use it
     * unless disk space constraints are critica
     *
     * @var bool
     */
    protected $noHeader = false;
    /**
     * If cryptography should be used to encode the source code
     *
     * @var bool
     */
    protected $useCrypto = false;
    /**
     * Force cooperation with other encoded files only. This option generates files that
     * work exclusively with associated encoded files. Associated encoded files are
     * those generated by the same company. Files that do not share the same encoded
     * company association cannot call these files
     *
     * @var bool
     */
    protected $encodedOnly = false;
    /**
     * Allow encoding previously encoded files. (NOT recommended!)
     *
     * @var bool
     */
    protected $forceEncode = false;
    /**
     * Make an encoded file to expire on the given date. Date is in yyyy-mm-dd format.
     *
     * @var string
     */
    protected $expires = null;
    /**
     * Level of obfuscation. Defaults to 0 (no obfuscation).
     *
     * @var int
     */
    protected $obfuscationLevel = 0;
    /**
     * Optimization mask. (default value: [+++++++])
     * opt_mask is an integer representing a bit-mask.
     * The default value enables all of the optimization passes.
     * Each optimization pass of the Zend Optimizer can be turned on or off based on
     * the mask entered
     *
     * @var int
     */
    protected $optMask = null;
    /**
     * Path to the zend encoder binary
     *
     * @var string
     */
    protected $zendEncoderPath = null;
    /**
     * Path to private key for licensing
     *
     * @var string
     */
    protected $privateKeyPath = null;
    /**
     * Enable licensing.
     * If enabled, productName must be defined.
     *
     * @var bool
     */
    protected $licenseProduct = false;
    /**
     * If true the ownership, permissions and timestamps
     * of the encoded files won't be preserved.
     *
     * @var bool
     */
    protected $ignoreFileModes = false;
    /**
     * Enable signing
     * If enabled, productName must be defined.
     *
     * @var bool
     */
    protected $signProduct = false;
    /**
     * Product name. Must be defined if licenseProduct
     * or signProduct is set to 1
     *
     * @var string
     */
    protected $productName = null;
    /**
     * Embed the information in the specified file into the header of the encoded file
     * (overrides noHeader)
     *
     * @var string
     */
    protected $prologFile = null;

    /**
     * TASK PROPERTIES SETTERS
     * @param $value
     */
    public function setZendEncoderPath($value)
    {
        $this->zendEncoderPath = $value;
    }

    /**
     * @param $value
     */
    public function setPrivateKeyPath($value)
    {
        $this->privateKeyPath = $value;
    }

    /**
     * @param $value
     */
    public function setShortTags($value)
    {
        $this->shortTags = (bool) $value;
    }

    /**
     * @param $value
     */
    public function setAspTags($value)
    {
        $this->aspTags = (bool) $value;
    }

    /**
     * @param $value
     */
    public function setDeleteSource($value)
    {
        $this->shortTags = (bool) $value;
    }

    /**
     * @param $value
     */
    public function setUseCrypto($value)
    {
        $this->useCrypto = (bool) $value;
    }

    /**
     * @param $value
     */
    public function setObfuscationLevel($value)
    {
        $this->obfuscationLevel = (int) $value;
    }

    /**
     * @param $value
     */
    public function setLicenseProduct($value)
    {
        $this->licenseProduct = (bool) $value;
    }

    /**
     * @param $value
     */
    public function setPrologFile($value)
    {
        $this->prologFile = $value;
    }

    /**
     * @param $value
     */
    public function setSignProduct($value)
    {
        $this->signProduct = (bool) $value;
    }

    /**
     * @param $value
     */
    public function setForceEncode($value)
    {
        $this->forceEncode = (bool) $value;
    }

    /**
     * @param $value
     */
    public function setEncodedOnly($value)
    {
        $this->encodedOnly = (bool) $value;
    }

    /**
     * @param $value
     */
    public function setIgnoreFileModes($value)
    {
        $this->ignoreFileModes = (bool) $value;
    }

    /**
     * @param $value
     */
    public function setExpires($value)
    {
        $this->expires = $value;
    }

    /**
     * @param $value
     */
    public function setProductName($value)
    {
        $this->productName = $value;
    }

    /**
     * @param $value
     */
    public function setOptMask($value)
    {
        $this->optMask = (int) $value;
    }

    /**
     * @param $value
     */
    public function setRenameSourceExt($value)
    {
        $this->renameSourceExt = $value;
    }

    /**
     * @param $value
     */
    public function setNoHeader($value)
    {
        $this->noHeader = (bool) $value;
    }

    /**
     * Add a new fileset.
     *
     * @return FileSet
     */
    public function createFileSet()
    {
        $this->fileset = new ZendGuardFileSet();
        $this->filesets[] = $this->fileset;

        return $this->fileset;
    }

    /**
     * Verifies that the configuration is correct
     *
     * @throws BuildException
     */
    protected function verifyConfiguration()
    {
        // Check that the zend encoder path is specified
        if (empty($this->zendEncoderPath)) {
            throw new BuildException("Zend Encoder path must be specified");
        }

        // verify that the zend encoder binary exists
        if (!file_exists($this->zendEncoderPath)) {
            throw new BuildException("Zend Encoder not found on path " . $this->zendEncoderPath);
        }

        // if either sign or license is required the private key path needs to be defined
        // and the file has to exist and product name has to be specified
        if ($this->signProduct || $this->licenseProduct) {
            if (empty($this->privateKeyPath)) {
                throw new BuildException("Licensing or signing requested but privateKeyPath not provided.");
            }
            if (!is_readable($this->privateKeyPath)) {
                throw new BuildException("Licensing or signing requested but private key path doesn't exist or is unreadable.");
            }
            if (empty($this->productName)) {
                throw new BuildException("Licensing or signing requested but product name not provided.");
            }
        }

        // verify prolog file exists
        if (!empty($this->prologFile)) {
            if (!file_exists($this->prologFile)) {
                throw new BuildException("The prolog file doesn't exist: " . $this->prologFile);
            }
        }
    }

    /**
     * Do the work
     *
     * @throws BuildException
     */
    public function main()
    {
        $this->verifyConfiguration();
        $this->prepareEncoderCommand();

        try {
            if (empty($this->filesets)) {
                throw new BuildException("You must supply nested fileset.",
                    $this->getLocation());
            }

            $encodedFilesCounter = 0;

            foreach ($this->filesets as $fs) {
                /* @var $fs FileSet */

                /* @var $fsBasedir PhingFile */
                $fsBasedir = $fs->getDir($this->project)->getAbsolutePath();

                $files = $fs->getFiles($this->project, false);

                foreach ($files as $file) {
                    $f = new PhingFile($fsBasedir, $file);

                    if ($f->isFile()) {
                        $path = $f->getAbsolutePath();

                        $this->log("Encoding " . $path, Project::MSG_VERBOSE);
                        $this->encodeFile($path);

                        $encodedFilesCounter++;
                    }
                }
            }

            $this->log("Encoded files: " . $encodedFilesCounter);
        } catch (IOException $ioe) {
            $msg = "Problem encoding files: " . $ioe->getMessage();
            throw new BuildException($msg, $ioe, $this->getLocation());
        }
    }

    /**
     * Prepares the main part of the command that will be
     * used to encode the given file(s).
     */
    protected function prepareEncoderCommand()
    {
        $command = $this->zendEncoderPath . " ";

        if (!empty($this->renameSourceExt)) {
            $command .= " --rename-source " . $this->renameSourceExt . " ";
        } elseif ($this->deleteSource) {
            // delete source
            $command .= " --delete-source ";
        }

        // short tags
        $command .= " --short-tags " . (($this->shortTags) ? 'on' : 'off') . " ";

        // asp tags
        $command .= " --asp-tags " . (($this->aspTags) ? 'on' : 'off') . " ";

        // use crypto
        if ($this->useCrypto) {
            $command .= " --use-crypto ";
        }

        // ignore file modes
        if ($this->ignoreFileModes) {
            $command .= " --ignore-file-modes ";
        }

        // force encode
        if ($this->forceEncode) {
            $command .= " --force-encode ";
        }

        // expires
        if (!empty($this->expires)) {
            $command .= " --expires " . $this->expires . " ";
        }

        // insert prolog file name or no-header
        if (!empty($this->prologFile)) {
            $command .= " --prolog-filename " . $this->prologFile . " ";
        } elseif ($this->noHeader) {
            // no-header
            $command .= " --no-header ";
        }

        // obfuscation level
        if ($this->obfuscationLevel > 0) {
            $command .= " --obfuscation-level " . $this->obfuscationLevel . " ";
        }

        // encoded only
        if ($this->encodedOnly) {
            $command .= " --encoded-only ";
        }

        // opt mask
        if (null !== $this->optMask) {
            $command .= " --optimizations " . $this->optMask . " ";
        }

        // Signing or licensing
        if ($this->signProduct) {
            $command .= " --sign-product " . $this->productName . " --private-key " . $this->privateKeyPath . " ";
        } elseif ($this->licenseProduct) {
            $command .= " --license-product " . $this->productName . " --private-key " . $this->privateKeyPath . " ";
        }

        // add a blank space
        $command .= " ";

        $this->encodeCommand = $command;

    }

    /**
     * Encodes a file using currently defined Zend Guard settings
     *
     * @param string $filePath Path to the encoded file
     * @throws BuildException
     * @return bool
     */
    protected function encodeFile($filePath)
    {
        $command = $this->encodeCommand . $filePath . ' 2>&1';

        $this->log('Running: ' . $command, Project::MSG_VERBOSE);

        $tmp = exec($command, $output, $return_var);
        if ($return_var !== 0) {
            throw new BuildException("Encoding failed. \n Msg: " . $tmp . " \n Encode command: " . $command);
        }

        return true;
    }

}

/**
 * This is a FileSet with the to specify permissions.
 *
 * Permissions are currently not implemented by PEAR Archive_Tar,
 * but hopefully they will be in the future.
 *
 * @package phing.tasks.ext.zendguard
 */
class ZendGuardFileSet extends FileSet
{
    private $files = null;

    /**
     *  Get a list of files and directories specified in the fileset.
     * @param Project $p
     * @param bool $includeEmpty
     * @throws BuildException
     * @return array a list of file and directory names, relative to
     *               the baseDir for the project.
     */
    public function getFiles(Project $p, $includeEmpty = true)
    {

        if ($this->files === null) {

            $ds = $this->getDirectoryScanner($p);
            $this->files = $ds->getIncludedFiles();
        } // if ($this->files===null)

        return $this->files;
    }

}
<?php

/*
 *  $Id: 2e1e3730b96f5d5ea32d54fda8d9333b34eeaaba $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Produce license files using Zeng Guard.
 * The task can produce a license file from the given
 * license properties or it can use a template.
 *
 * @author    Petr Rybak <petr@rynawe.net>
 * @version   $Id: 2e1e3730b96f5d5ea32d54fda8d9333b34eeaaba $
 * @package   phing.tasks.ext.zendguard
 * @since     2.4.3
 */
class ZendGuardLicenseTask extends Task
{
    protected $zendsignCommand;
    private $tmpLicensePath;

    /**
     * TASK PROPERTIES
     *
     * See http://static.zend.com/topics/Zend-Guard-User-Guidev5x.pdf
     * for more information on how to use ZendGuard
     *
     */
    /**
     * Path to Zend Guard zendenc_sign executable
     *
     * @var string
     */
    protected $zendsignPath;
    /**
     * Path to private key that will be used to sign the license
     *
     * @var string
     */
    protected $privateKeyPath;
    /**
     * Where to store the signed license file
     *
     * @var string
     */
    protected $outputFile;
    /**
     * Path to license template. If specified all
     * license properties will be ignored and the
     * template will be used to generate the file.
     *
     * @var string
     */
    protected $licenseTemplate;
    /**
     * The name assigned to Product. This must be the same name used when encoding
     * the PHP files.
     *
     * REQUIRED
     *
     * @var string
     */
    protected $productName;
    /**
     * The Name of the Registered owner of the license.
     *
     * REQUIRED
     *
     * @var string
     */
    protected $registeredTo;
    /**
     * Expiration date of the license. Used if the license is issued with a date restriction.
     * Possible values:
     *     - 'Never', '0' or false: the license won't expire
     *     - A Date in format DD-MM-YYYY to set expiration for that date
     *     - Relative date supported by the PHP strtotime function (e.g. +1 month)
     *
     * REQUIRED
     *
     * @var string
     */
    protected $expires;
    /**
     * Limits the use of the license to IP addresses that fall within specification. Supports
     * wildcards for any of the IP place holders, as well as the two types of net masks
     * (filters).
     * Netmask pair An IP a.b.c.d, and a netmask w.x.y.z. (That is., 10.1.0.0/255.255.0.0),
     * where the binary of mask is applied to filter IP addresses.
     * ip/nnn (similar to a CIDR specification) This mask consists of nnn high-order 1 bits.
     * (That is, 10.1.0.0/16 is the same as 10.1.0.0/255.255.0.0). Instead of spelling out
     * the bits of the subnet mask, this mask notation is simply listed as the number of 1s
     * bits that start the mask. Rather than writing the address and subnet mask as
     * 192.60.128.0/255.255.252.0 the network address would be written simply as:
     * 192.60.128.0/22 which indicates starting address of the network and number of 1s
     * bits (22) in the network portion of the address. The mask in binary is
     * (11111111.11111111.11111100.00000000).
     *
     * OPTIONAL
     *
     * Example (Wildcard):
     * IP-Range = 10.1.*.*
     * Example (Net Mask):
     * IP-Range = 10.1.0.0/255.255.0.0
     * Example (Net Mask):
     * IP-Range = 10.1.0.0/16
     *
     * @var string
     */
    protected $ipRange;
    /**
     * Coded string (Zend Host ID) used to lock the license to a specific hardware. The
     * Zend Host ID obtained from the machine where the encoded files and license are
     * to be installed. The Zend Host ID code can be obtained by using the zendid utility.
     * For more details, see Getting the Zend Host ID.
     *
     * REQUIRED if Hardware-Locked is set equal to YES.
     * Meaningless if Hardware-Locked is set equal to NO.
     *
     * User semicolon to enter more than one Host-ID
     *
     * Example:
     * Host-ID = H:MFM43-Q9CXC-B9EDX-GWYSU;H:MFM43-Q9CXC-B9EDX-GWYTY
     *
     * @var string
     */
    protected $hostID;
    /**
     * Option that indicates if the license will be locked to a specific machine
     * using the Zend Host ID code(s). If set to YES, the Host-ID is required.
     *
     * OPTIONAL
     *
     * @var bool
     */
    protected $hardwareLocked;
    /**
     * Semi-colon separated user defined values that will be part of the license. These values
     * CANNOT be modified after the license is produced. Modification
     * would invalidate the license.
     *
     * OPTIONAL
     * Example:
     * Tea=Mint Flavor;Coffee=Arabica
     *
     * @var string
     */
    protected $userDefinedValues;
    /**
     * Semi-colon separated user defined x-values that will be part of the license. These values
     * CAN be modified after the license is produced. Modification
     * won't invalidate the license.
     *
     * OPTIONAL
     * Example:
     * Tea=Mint Flavor;Coffee=Arabica
     *
     * @var string
     */
    protected $xUserDefinedValues;

    /**
     * @param $value
     */
    public function setLicenseTemplate($value)
    {
        $this->licenseTemplate = $value;
    }

    /**
     * @param $productName
     */
    public function setProductName($productName)
    {
        $this->productName = $productName;
    }

    /**
     * @param $registeredTo
     */
    public function setRegisteredTo($registeredTo)
    {
        $this->registeredTo = $registeredTo;
    }

    /**
     * Process the expires property. If the value is
     * empty (false, '', ...) it will set the value to 'Never'
     * Otherwise it will run the value through strtotime so relative
     * date and time notation can be used (e.g. +1 month)
     *
     * @param mixed $expires
     *
     * @throws BuildException
     * @return string
     */
    public function setExpires($expires)
    {
        // process the expires value
        if (false === $expires || '0' === $expires || strtolower($expires) == 'never' || '' === $expires) {
            $this->expires = 'Never';
        } else {
            $time = strtotime($expires);
            if (!$time) {
                throw new BuildException("Unsupported expires format: " . $expires);
            }
            $this->expires = date('d-M-Y', $time);
        }
    }

    /**
     * @param $iprange
     */
    public function setIpRange($iprange)
    {
        $this->ipRange = $iprange;
    }

    /**
     * @param $hostID
     */
    public function setHostID($hostID)
    {
        $this->hostID = $hostID;
    }

    /**
     * @param $hardwareLocked
     */
    public function setHardwareLocked($hardwareLocked)
    {
        $this->hardwareLocked = (bool) $hardwareLocked;
    }

    /**
     * @param $userDefinedValues
     */
    public function setUserDefinedValues($userDefinedValues)
    {
        $this->userDefinedValues = $userDefinedValues;
    }

    /**
     * @param $xUserDefinedValues
     */
    public function setXUserDefinedValues($xUserDefinedValues)
    {
        $this->xUserDefinedValues = $xUserDefinedValues;
    }

    /**
     * @param $zendsignPath
     */
    public function setZendsignPath($zendsignPath)
    {
        $this->zendsignPath = $zendsignPath;
    }

    /**
     * @param $privateKeyPath
     */
    public function setPrivateKeyPath($privateKeyPath)
    {
        $this->privateKeyPath = $privateKeyPath;
    }

    /**
     * @param $outputFile
     */
    public function setOutputFile($outputFile)
    {
        $this->outputFile = $outputFile;
    }

    /**
     * Verifies that the configuration is correct
     *
     * @throws BuildException
     */
    protected function verifyConfiguration()
    {
        // Check that the zend encoder path is specified
        if (empty($this->zendsignPath)) {
            throw new BuildException("Zendenc_sign path must be specified");
        }
        // verify that the zend encoder binary exists
        if (!file_exists($this->zendsignPath)) {
            throw new BuildException("Zendenc_sign not found on path " . $this->zendsignPath);
        }

        // verify that the private key path is defined
        if (empty($this->privateKeyPath)) {
            throw new BuildException("You must define privateKeyPath.");
        }
        // verify that the private key file is readable
        if (!is_readable($this->privateKeyPath)) {
            throw new BuildException("Private key file is not readable: " . $this->privateKeyPath);
        }

        // if template is passed, verify that it is readable
        if (!empty($this->licenseTemplate)) {
            if (!is_readable($this->licenseTemplate)) {
                throw new BuildException("License template file is not readable " . $this->licenseTemplate);
            }
        }

        // check that output file path is defined
        if (empty($this->outputFile)) {
            throw new BuildException("Path where to store the result file needs to be defined in outputFile property");
        }

        // if license template is NOT provided check that all required parameters are defined
        if (empty($this->licenseTemplate)) {

            // check productName
            if (empty($this->productName)) {
                throw new BuildException("Property must be defined: productName");
            }

            // check expires
            if (null === $this->expires) {
                throw new BuildException("Property must be defined: expires");
            }

            // check registeredTo
            if (empty($this->registeredTo)) {
                throw new BuildException("Property must be defined: registeredTo");
            }

            // check hardwareLocked
            if (null === $this->hardwareLocked) {
                throw new BuildException("Property must be defined: hardwareLocked");
            }

            // if hardwareLocked is set to true, check that Host-ID is set
            if ($this->hardwareLocked) {
                if (empty($this->hostID)) {
                    throw new BuildException("If you set hardwareLocked to true hostID must be provided");
                }
            }
        }
    }

    /**
     * Do the work
     *
     * @throws BuildException
     */
    public function main()
    {
        try {
            $this->verifyConfiguration();

            $this->generateLicense();
        } catch (Exception $e) {
            // remove the license temp file if it was created
            $this->cleanupTmpFiles();

            throw $e;
        }
        $this->cleanupTmpFiles();
    }

    /**
     * If temporary license file was created during the process
     * this will remove it
     *
     * @return void
     */
    private function cleanupTmpFiles()
    {
        if (!empty($this->tmpLicensePath) && file_exists($this->tmpLicensePath)) {
            $this->log("Deleting temporary license template " . $this->tmpLicensePath, Project::MSG_VERBOSE);

            unlink($this->tmpLicensePath);
        }
    }

    /**
     * Prepares and returns the command that will be
     * used to create the license.
     *
     * @return string
     */
    protected function prepareSignCommand()
    {
        $command = $this->zendsignPath;

        // add license path
        $command .= ' ' . $this->getLicenseTemplatePath();

        // add result file path
        $command .= ' ' . $this->outputFile;

        // add key path
        $command .= ' ' . $this->privateKeyPath;

        $this->zendsignCommand = $command;

        return $command;
    }

    /**
     * Checks if the license template path is defined
     * and returns it.
     * If it the license template path is not defined
     * it will generate a temporary template file and
     * provide it as a template.
     *
     * @return string
     */
    protected function getLicenseTemplatePath()
    {
        if (!empty($this->licenseTemplate)) {
            return $this->licenseTemplate;
        } else {
            return $this->generateLicenseTemplate();
        }
    }

    /**
     * Creates the signed license at the defined output path
     *
     * @throws BuildException
     * @return void
     */
    protected function generateLicense()
    {
        $command = $this->prepareSignCommand() . ' 2>&1';

        $this->log('Creating license at ' . $this->outputFile);

        $this->log('Running: ' . $command, Project::MSG_VERBOSE);
        $tmp = exec($command, $output, $return_var);

        // Check for exit value 1. Zendenc_sign command for some reason
        // returns 0 in case of failure and 1 in case of success...
        if ($return_var !== 1) {
            throw new BuildException("Creating license failed. \n\nZendenc_sign msg:\n" . join("\n", $output) . "\n\n");
        }
    }

    /**
     * It will generate a temporary license template
     * based on the properties defined.
     *
     * @throws BuildException
     * @return string Path of the temporary license template file
     */
    protected function generateLicenseTemplate()
    {
        $this->tmpLicensePath = tempnam(sys_get_temp_dir(), 'zendlicense');

        $this->log("Creating temporary license template " . $this->tmpLicensePath, Project::MSG_VERBOSE);
        if (file_put_contents($this->tmpLicensePath, $this->generateLicenseTemplateContent()) === false) {
            throw new BuildException("Unable to create temporary template license file: " . $this->tmpLicensePath);
        }

        return $this->tmpLicensePath;
    }

    /**
     * Generates license template content based
     * on the defined parameters
     *
     * @return string
     */
    protected function generateLicenseTemplateContent()
    {
        $contentArr = array();

        // Product Name
        $contentArr[] = array('Product-Name', $this->productName);
        // Registered to
        $contentArr[] = array('Registered-To', $this->registeredTo);
        // Hardware locked
        $contentArr[] = array('Hardware-Locked', ($this->hardwareLocked ? 'Yes' : 'No'));

        // Expires
        $contentArr[] = array('Expires', $this->expires);

        // IP-Range
        if (!empty($this->ipRange)) {
            $contentArr[] = array('IP-Range', $this->ipRange);
        }
        // Host-ID
        if (!empty($this->hostID)) {
            foreach (explode(';', $this->hostID) as $hostID) {
                $contentArr[] = array('Host-ID', $hostID);
            }
        } else {
            $contentArr[] = array('Host-ID', 'Not-Locked');
        }

        // parse user defined fields
        if (!empty($this->userDefinedValues)) {
            $this->parseAndAddUserDefinedValues($this->userDefinedValues, $contentArr);
        }
        // parse user defined x-fields
        if (!empty($this->xUserDefinedValues)) {
            $this->parseAndAddUserDefinedValues($this->xUserDefinedValues, $contentArr, 'X-');
        }

        // merge all the values
        $content = '';
        foreach ($contentArr as $valuePair) {

            list($key, $value) = $valuePair;

            $content .= $key . " = " . $value . "\n";
        }

        return $content;
    }

    /**
     * Parse the given string in format like key1=value1;key2=value2;... and
     * converts it to array
     *   (key1=>value1, key2=value2, ...)
     *
     * @param string $valueString Semi-colon separated value pairs
     * @param array $valueArray Array to which the values will be added
     * @param string $keyPrefix Prefix to use when adding the key
     *
     * @param string $pairSeparator
     * @return void
     */
    protected function parseAndAddUserDefinedValues(
        $valueString,
        array &$valueArray,
        $keyPrefix = '',
        $pairSeparator = ';'
    ) {
        // explode the valueString (semicolon)
        $valuePairs = explode($pairSeparator, $valueString);
        if (!empty($valuePairs)) {
            foreach ($valuePairs as $valuePair) {
                list($key, $value) = explode('=', $valuePair, 2);

                // add pair into the valueArray
                $valueArray[] = array($keyPrefix . $key, $value);
            }
        }
    }

}
<?php
/**
 * $Id: e456c8757c8c6d9d2971d63d37d8347a86cc46a0 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Class ZendServerDeploymentToolTask
 *
 * @author Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.tasks.ext.zendserverdevelopmenttools
 */
abstract class zsdtBaseTask extends Task
{
    protected $action;

    protected $arguments = '';

    /** @var string $descriptor */
    protected $descriptor;

    /** @var string $schema */
    protected $schema;

    /** @var array $path */
    private $path = array(
        'NIX' => '/usr/local/zend/bin/zdpack',
        'WIN' => 'C:\Program Files (x86)\Zend\ZendServer\bin\zdpack',
        'USR' => ''
    );

    /**
     * The package descriptor file.
     *
     * @param string $descriptor
     *
     * @return void
     */
    public function setDescriptor($descriptor)
    {
        $this->descriptor = escapeshellarg($descriptor);
    }

    /**
     * The path to the package descriptor schema used for validation.
     *
     * @param string $schema
     *
     * @return void
     */
    public function setSchema($schema)
    {
        $this->schema = escapeshellarg($schema);
    }

    /**
     * @param string $path
     *
     * @return void
     */
    public function setPath($path)
    {
        $this->path['USR'] = $path;
    }

    /**
     * {@inheritdoc}
     */
    public function main()
    {
        $this->validate();

        $command = '';
        if ($this->path['USR'] !== '') {
            $command .= $this->path['USR'];
        } elseif (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
            $command .= escapeshellarg($this->path['WIN']);
        } else {
            $command .= $this->path['NIX'];
        }

        $commandString = sprintf('%s %s %s', $command, $this->action, $this->arguments);
        $msg = exec($commandString . ' 2>&1', $output, $code);

        if ($code !== 0) {
            throw new BuildException("Build package failed. \n Msg: " . $msg . " \n Pack command: " . $commandString);
        }
    }

    /**
     * Validates argument list.
     *
     * @return void
     */
    protected function validate()
    {
        if ($this->schema !== null) {
            $this->arguments .= "--schema=$this->schema ";
        }
    }
}
<?php
/**
 * $Id: 5fdaa20ab4e93edbfddc6bd90e39655e020c4197 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/zendserverdeploymenttool/zsdtBaseTask.php';

/**
 * Class ZendServerDeploymentToolTask
 *
 * @author Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.tasks.ext.zendserverdevelopmenttools
 */
class zsdtPackTask extends zsdtBaseTask
{
    /** @var string $package */
    private $package;

    /** @var string $source */
    private $source;

    /** @var string $scripts */
    private $scripts;

    /** @var string $output */
    private $output;

    /** @var string $phpbin */
    private $phpbin;

    /** @var bool $lint */
    private $lint = false;

    /**
     * A directory containing the data and the script directories, in addition to the package descriptor file.
     *
     * @param string $package
     *
     * @return void
     */
    public function setPackage($package)
    {
        $this->package = escapeshellarg($package);
    }

    /**
     * Performs a PHP lint test on the deployment scripts before creating the package.
     *
     * @param boolean $lint
     *
     * @return void
     */
    public function setLint($lint)
    {
        $this->lint = $lint;
    }

    /**
     * The directory in which the package is created.
     * The package name will be created as "<app-name>-<app-version>.zpk".
     *
     * @param string $output
     *
     * @return void
     */
    public function setOutput($output)
    {
        $this->output = escapeshellarg($output);
    }

    /**
     * The PHP executable to use for lint.
     *
     * @param string $phpbin
     *
     * @return void
     */
    public function setPhpbin($phpbin)
    {
        $this->phpbin = escapeshellarg($phpbin);
    }

    /**
     * The directory which contains the package deployment scripts.
     * The Deployment Tool will search this directory for the expected files and then packs them.
     *
     * @param string $scripts
     *
     * @return void
     */
    public function setScripts($scripts)
    {
        $this->scripts = escapeshellarg($scripts);
    }
    /**
     * The directory that contains the application resources (PHP sources, JavaScript, etc.).
     * The directory's internal structure must match the necessary structure for the application to be functional.
     *
     * @param string $source
     *
     * @return void
     */
    public function setSource($source)
    {
        $this->source = escapeshellarg($source);
    }

    /**
     * {@inheritdoc}
     *
     * @return void
     */
    public function init()
    {
        $this->action = 'pack';
    }

    /**
     * {@inheritdoc}
     *
     * @return void
     *
     * @throws BuildException
     */
    protected function validate()
    {
        if ($this->descriptor === null || $this->scripts === null || $this->package === null) {
            throw new BuildException(
                'The deployment tool needs at least the project descriptor, '
                . 'the scripts folder and package folder to be set.'
            );
        } elseif ($this->lint !== false && $this->phpbin === null) {
            throw new BuildException('You set the lint option but not the path to the php executable.');
        }

        parent::validate();

        if ($this->lint !== false) {
            $this->arguments .= '--lint ';
        }

        if ($this->source !== null) {
            $this->arguments .= "--src-dir=$this->source ";
        }

        if ($this->output !== null) {
            $this->arguments .= "--output-dir=$this->output ";
        }

        if ($this->phpbin !== null) {
            $this->arguments .= "--php-exe=$this->phpbin ";
        }

        $this->arguments .= "--scripts-dir=$this->scripts ";
        $this->arguments .= "--package-descriptor=$this->descriptor ";
        $this->arguments .= $this->package;
    }
}
<?php
/**
 * $Id: 53fe9d2cbd622bf129a44c1e06c9bae8fe14fc89 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/ext/zendserverdeploymenttool/zsdtBaseTask.php';

/**
 * Class ZendServerDeploymentToolTask
 *
 * @author Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.tasks.ext.zendserverdevelopmenttools
 */
class zsdtValidateTask extends zsdtBaseTask
{
    /**
     * @inheritdoc}
     *
     * @return void
     */
    public function init()
    {
        $this->action = 'validate';
    }

    /**
     * {@inheritdoc}
     *
     * @throws BuildException
     *
     * @return void
     */
    protected function validate()
    {
        parent::validate();

        if ($this->descriptor === null) {
            throw new BuildException('The package descriptor file have to be set.');
        }

        $this->arguments .= $this->descriptor;
    }
}
<?php
/*
 *  $Id: 3e199443346a7e474e65c407ba691e5bdaf40f5a $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/MatchingTask.php';
include_once 'phing/util/SourceFileScanner.php';
include_once 'phing/mappers/MergeMapper.php';
include_once 'phing/util/StringHelper.php';

/**
 * Creates a zip archive using PHP ZipArchive extension/
 *
 * @author    Michiel Rook <mrook@php.net>
 * @version   $Id: 3e199443346a7e474e65c407ba691e5bdaf40f5a $
 * @package   phing.tasks.ext
 * @since     2.1.0
 */
class ZipTask extends MatchingTask
{

    /**
     * @var PhingFile
     */
    private $zipFile;

    /**
     * @var PhingFile
     */
    private $baseDir;

    /**
     * Whether to include empty dirs in the archive.
     */
    private $includeEmpty = true;

    private $filesets = array();
    
    private $ignoreLinks = false;

    /**
     * File path prefix in zip archive
     *
     * @var string
     */
    private $prefix = null;

    /**
     * Comment for zip archive.
     *
     * @var string $comment
     */
    private $comment = '';

    /**
     * Add a new fileset.
     * @return FileSet
     */
    public function createFileSet()
    {
        $this->fileset = new ZipFileSet();
        $this->filesets[] = $this->fileset;

        return $this->fileset;
    }

    /**
     * Set is the name/location of where to create the zip file.
     * @param PhingFile $destFile The output of the zip
     */
    public function setDestFile(PhingFile $destFile)
    {
        $this->zipFile = $destFile;
    }

    /**
     * This is the base directory to look in for things to zip.
     * @param PhingFile $baseDir
     */
    public function setBasedir(PhingFile $baseDir)
    {
        $this->baseDir = $baseDir;
    }

    /**
     * Sets the file path prefix for file in the zip file.
     *
     * @param string $prefix Prefix
     *
     * @return void
     */
    public function setPrefix($prefix)
    {
        $this->prefix = $prefix;
    }

    /**
     * Set the include empty dirs flag.
     * @param  boolean  Flag if empty dirs should be tarred too
     * @return void
     */
    public function setIncludeEmptyDirs($bool)
    {
        $this->includeEmpty = (boolean) $bool;
    }

    /**
     * Set the ignore symlinks flag.
     * @param  boolean $bool Flag if symlinks should be ignored
     * @return void
     */
    public function setIgnoreLinks($bool)
    {
        $this->ignoreLinks = (boolean) $bool;
    }

    /**
     * Add a comment to the zip archive.
     *
     * @param string $text
     *
     * @return void
     */
    public function setComment($text)
    {
        $this->comment = $text;
    }

    /**
     * do the work
     * @throws BuildException
     */
    public function main()
    {
        if (!extension_loaded('zip')) {
            throw new BuildException("Zip extension is required");
        }

        if ($this->zipFile === null) {
            throw new BuildException("zipfile attribute must be set!", $this->getLocation());
        }

        if ($this->zipFile->exists() && $this->zipFile->isDirectory()) {
            throw new BuildException("zipfile is a directory!", $this->getLocation());
        }

        if ($this->zipFile->exists() && !$this->zipFile->canWrite()) {
            throw new BuildException("Can not write to the specified zipfile!", $this->getLocation());
        }

        try {
            if ($this->baseDir !== null) {
                if (!$this->baseDir->exists()) {
                    throw new BuildException("basedir '" . (string) $this->baseDir . "' does not exist!", $this->getLocation());
                }

                if (empty($this->filesets)) {
                    // add the main fileset to the list of filesets to process.
                    $mainFileSet = new ZipFileSet($this->fileset);
                    $mainFileSet->setDir($this->baseDir);
                    $this->filesets[] = $mainFileSet;
                }
            }

            if (empty($this->filesets)) {
                throw new BuildException("You must supply either a basedir "
                    . "attribute or some nested filesets.",
                    $this->getLocation());
            }

            // check if zip is out of date with respect to each
            // fileset
            if ($this->areFilesetsUpToDate()) {
                $this->log("Nothing to do: " . $this->zipFile->__toString() . " is up to date.", Project::MSG_INFO);

                return;
            }

            $this->log("Building zip: " . $this->zipFile->__toString(), Project::MSG_INFO);

            $zip = new ZipArchive();
            $res = $zip->open($this->zipFile->getAbsolutePath(), ZIPARCHIVE::CREATE);

            if ($res !== true) {
                throw new Exception("ZipArchive::open() failed with code " . $res);
            }

            if ($this->comment !== '') {
                $isCommented = $zip->setArchiveComment($this->comment);
                if ($isCommented === false) {
                    $this->log("Could not add a comment for the Archive.", Project::MSG_INFO);
                }
            }

            $this->addFilesetsToArchive($zip);

            $zip->close();
        } catch (IOException $ioe) {
            $msg = "Problem creating ZIP: " . $ioe->getMessage();
            throw new BuildException($msg, $ioe, $this->getLocation());
        }
    }

    /**
     * @param  array     $files array of filenames
     * @param  PhingFile $dir
     * @return boolean
     */
    private function archiveIsUpToDate($files, $dir)
    {
        $sfs = new SourceFileScanner($this);
        $mm = new MergeMapper();
        $mm->setTo($this->zipFile->getAbsolutePath());

        return count($sfs->restrict($files, $dir, null, $mm)) == 0;
    }

    /**
     * @return array
     * @throws BuildException
     */
    public function areFilesetsUpToDate()
    {
        foreach ($this->filesets as $fs) {
            $files = $fs->getFiles($this->project, $this->includeEmpty);
            if (!$this->archiveIsUpToDate($files, $fs->getDir($this->project))) {
                return false;
            }
            for ($i = 0, $fcount = count($files); $i < $fcount; $i++) {
                if ($this->zipFile->equals(new PhingFile($fs->getDir($this->project), $files[$i]))) {
                    throw new BuildException("A zip file cannot include itself", $this->getLocation());
                }
            }
        }
        return true;
    }

    /**
     * @param $zip
     */
    private function addFilesetsToArchive($zip)
    {
        foreach ($this->filesets as $fs) {
            $fsBasedir = (null != $this->baseDir) ? $this->baseDir :
                $fs->getDir($this->project);

            $files = $fs->getFiles($this->project, $this->includeEmpty);

            for ($i = 0, $fcount = count($files); $i < $fcount; $i++) {
                $f = new PhingFile($fsBasedir, $files[$i]);

                $pathInZip = $this->prefix
                    . $f->getPathWithoutBase($fsBasedir);

                $pathInZip = str_replace('\\', '/', $pathInZip);

                if ($this->ignoreLinks && $f->isLink()) {
                    continue;
                } elseif ($f->isDirectory()) {
                    if ($pathInZip != '.') {
                        $zip->addEmptyDir($pathInZip);
                    }
                } else {
                    $zip->addFile($f->getAbsolutePath(), $pathInZip);
                }
                $this->log("Adding " . $f->getPath() . " as " . $pathInZip . " to archive.", Project::MSG_VERBOSE);
            }
        }
    }

}

/**
 * This is a FileSet with the to specify permissions.
 *
 * Permissions are currently not implemented by PEAR Archive_Tar,
 * but hopefully they will be in the future.
 *
 * @package phing.tasks.ext
 */
class ZipFileSet extends FileSet
{

    private $files = null;

    /**
     *  Get a list of files and directories specified in the fileset.
     * @param Project $p
     * @param bool $includeEmpty
     * @throws BuildException
     * @return array a list of file and directory names, relative to
     *               the baseDir for the project.
     */
    public function getFiles(Project $p, $includeEmpty = true)
    {

        if ($this->files === null) {

            $ds = $this->getDirectoryScanner($p);
            $this->files = $ds->getIncludedFiles();

            // build a list of directories implicitly added by any of the files
            $implicitDirs = array();
            foreach ($this->files as $file) {
                $implicitDirs[] = dirname($file);
            }

            $incDirs = $ds->getIncludedDirectories();

            // we'll need to add to that list of implicit dirs any directories
            // that contain other *directories* (and not files), since otherwise
            // we get duplicate directories in the resulting tar
            foreach ($incDirs as $dir) {
                foreach ($incDirs as $dircheck) {
                    if (!empty($dir) && $dir == dirname($dircheck)) {
                        $implicitDirs[] = $dir;
                    }
                }
            }

            $implicitDirs = array_unique($implicitDirs);

            $emptyDirectories = array();

            if ($includeEmpty) {
                // Now add any empty dirs (dirs not covered by the implicit dirs)
                // to the files array.

                foreach ($incDirs as $dir) { // we cannot simply use array_diff() since we want to disregard empty/. dirs
                    if ($dir != "" && $dir != "." && !in_array($dir, $implicitDirs)) {
                        // it's an empty dir, so we'll add it.
                        $emptyDirectories[] = $dir;
                    }
                }
            } // if $includeEmpty

            $this->files = array_merge($implicitDirs, $emptyDirectories, $this->files);
            sort($this->files);
        } // if ($this->files===null)

        return $this->files;
    }
}
<?php
/*
 *  $Id: d675b0dcedf7cc852b753abcad05a1cc3ba1e99b $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Abstract class for creating adhoc Phing components in buildfile.
 *
 * By itself this class can be used to declare a single class within your buildfile.
 * You can then reference this class in any task that takes custom classes (selectors,
 * mappers, filters, etc.)
 *
 * Subclasses exist for conveniently declaring and registering tasks and types.
 *
 * @author   Hans Lellelid <hans@xmpl.org>
 * @version  $Id: d675b0dcedf7cc852b753abcad05a1cc3ba1e99b $
 * @package  phing.tasks.system
 */
class AdhocTask extends Task
{

    /**
     * The PHP script
     * @var string
     */
    protected $script;

    protected $newClasses = array();

    /**
     * Main entry point
     */
    public function main()
    {
        $this->execute();
        if ($this->newClasses) {
            foreach ($this->newClasses as $classname) {
                $this->log("Added adhoc class " . $classname, Project::MSG_VERBOSE);
            }
        } else {
            $this->log("Adhoc task executed but did not result in any new classes.", Project::MSG_VERBOSE);
        }
    }

    /**
     * Get array of names of newly defined classes.
     * @return array
     */
    protected function getNewClasses()
    {
        return $this->newClasses;
    }

    /**
     * Load the adhoc class, and perform any core validation.
     * @return string         The classname of the ProjectComponent class.
     * @throws BuildException - if more than one class is defined.
     */
    protected function execute()
    {
        $classes = get_declared_classes();
        eval($this->script);
        $this->newClasses = array_diff(get_declared_classes(), $classes);
    }

    /**
     * Set the script.
     * @param string $script
     */
    public function addText($script)
    {
        $this->script = $script;
    }

}
<?php

/*
 * $Id: c0cdcd2c07fd4b81ac8f1b7cbc50a4031b3d5ddd $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/AdhocTask.php';

/**
 * A class for creating adhoc tasks in build file.
 *
 * <target name="test-adhoc">
 *        <adhoc-task name="foo"><![CDATA[
 *
 *            class FooTest extends Task {
 *                private $bar;
 *
 *                function setBar($bar) {
 *                    $this->bar = $bar;
 *                }
 *
 *                function main() {
 *                    $this->log("In FooTest: " . $this->bar);
 *                }
 *            }
 *
 *        ]]></adhoc-task>
 *
 *      <foo bar="B.L.I.N.G"/>
 * </target>
 *
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: c0cdcd2c07fd4b81ac8f1b7cbc50a4031b3d5ddd $
 * @package   phing.tasks.system
 */
class AdhocTaskdefTask extends AdhocTask
{

    /**
     * The tag that refers to this task.
     */
    private $name;

    /**
     * Set the tag that will represent this adhoc task/type.
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /** Main entry point */
    public function main()
    {
        if ($this->name === null) {
            throw new BuildException("The name attribute is required for adhoc task definition.", $this->location);
        }

        $taskdefs = $this->getProject()->getTaskDefinitions();

        if (!isset($taskdefs[$this->name])) {
            $this->execute();

            $classes = $this->getNewClasses();

            if (count($classes) < 1) {
                throw new BuildException("You must define at least one class for AdhocTaskdefTask.");
            }

            $classname = array_pop($classes);

            // instantiate it to make sure it is an instance of Task
            $t = new $classname();
            if (!($t instanceof Task)) {
                throw new BuildException("The adhoc class you defined must be an instance of phing.Task", $this->location);
            }

            $this->log("Task " . $this->name . " will be handled by class " . $classname, Project::MSG_VERBOSE);
            $this->project->addTaskDefinition($this->name, $classname);
        }
    }
}
<?php

/*
 * $Id: fa92d7af9fd72f34ae578ca4e8ae4d1de9a80ff5 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/AdhocTask.php';

/**
 * A class for creating adhoc datatypes in build file.
 *
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: fa92d7af9fd72f34ae578ca4e8ae4d1de9a80ff5 $
 * @package   phing.tasks.system
 */
class AdhocTypedefTask extends AdhocTask
{

    /**
     * The tag that refers to this task.
     */
    private $name;

    /**
     * Set the tag that will represent this adhoc task/type.
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /** Main entry point */
    public function main()
    {

        if ($this->name === null) {
            throw new BuildException("The name attribute is required for adhoc task definition.", $this->location);
        }

        $this->execute();

        $classes = $this->getNewClasses();
        if (count($classes) !== 1) {
            throw new BuildException("You must define one (and only one) class for AdhocTypedefTask.");
        }
        $classname = array_shift($classes);

        // instantiate it to make sure it is an instance of ProjectComponent
        $t = new $classname();
        if (!($t instanceof ProjectComponent)) {
            throw new BuildException("The adhoc class you defined must be an instance of phing.ProjectComponent", $this->location);
        }

        $this->log("Datatype " . $this->name . " will be handled by class " . $classname, Project::MSG_VERBOSE);
        $this->project->addDataTypeDefinition($this->name, $classname);
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Text element points to a file or contains text.
 * @package phing.tasks.system.AppendTask
 */
class TextElement extends ProjectComponent
{
    public $value = "";
    public $trimLeading = false;
    public $trim = false;
    public $filtering = true;
    public $encoding = null;

    /**
     * whether to filter the text in this element
     * or not.
     *
     * @param filtering true if the text should be filtered.
     *                  the default value is true.
     */
    public function setFiltering($filtering)
    {
        $this->filtering = $filtering;
    }

    /** return the filtering attribute */
    private function getFiltering()
    {
        return $this->filtering;
    }

    /**
     * The encoding of the text element
     *
     * @param encoding the name of the charset used to encode
     */
    public function setEncoding($encoding)
    {
        $this->encoding = $encoding;
    }

    /**
     * set the text using a file
     * @param file the file to use
     * @throws BuildException if the file does not exist, or cannot be
     *                        read
     */
    public function setFile(PhingFile $file)
    {
        // non-existing files are not allowed
        if (!$file->exists()) {
            throw new BuildException("File " . $file . " does not exist.");
        }

        $reader = null;
        try {
            if ($this->encoding == null) {
                $reader = new BufferedReader(new FileReader($file));
            } else {
                $reader = new BufferedReader(
                    new InputStreamReader(new FileInputStream($file))
                );
            }
            $this->value = $reader->read();
        } catch (IOException $ex) {
            $reader->close();
            throw new BuildException($ex);
        }
        $reader->close();
    }

    /**
     * set the text using inline
     * @param value the text to place inline
     */
    public function addText($value)
    {
        $this->value .= $this->getProject()->replaceProperties($value);
    }

    /**
     * s:^\s*:: on each line of input
     * @param strip if true do the trim
     */
    public function setTrimLeading($strip)
    {
        $this->trimLeading = $strip;
    }

    /**
     * whether to call text.trim()
     * @param trim if true trim the text
     */
    public function setTrim($trim)
    {
        $this->trim = $trim;
    }

    /**
     * @return the text, after possible trimming
     */
    public function getValue()
    {
        if ($this->value == null) {
            $this->value = "";
        }
        if (trim($this->value) === '') {
            $this->value = "";
        }
        if ($this->trimLeading) {
            $current = str_split($this->value);
                $b = '';
                $startOfLine = true;
                $pos = 0;
                while ($pos < count($current)) {
                    $ch = $current[$pos++];
                    if ($startOfLine) {
                        if ($ch == ' ' || $ch == "\t") {
                            continue;
                        }
                        $startOfLine = false;
                    }
                    $b .= $ch;
                    if ($ch == "\n" || $ch == "\r") {
                        $startOfLine = true;
                    }
                }
                $this->value = $b;
            }
        if ($this->trim) {
            $this->value = trim($this->value);
        }
        return $this->value;
    }
}
<?php
/*
 *  $Id: d9dc56999ea13b1699b1cc615998eb6c7a795bb4 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/types/FileList.php';
include_once 'phing/types/FileSet.php';
include_once 'phing/tasks/system/AppendTask/TextElement.php';

/**
 *  Appends text, contents of a file or set of files defined by a filelist to a destination file.
 *
 * <code>
 * <append text="And another thing\n" destfile="badthings.log"/>
 * </code>
 * OR
 * <code>
 * <append file="header.html" destfile="fullpage.html"/>
 * <append file="body.html" destfile="fullpage.html"/>
 * <append file="footer.html" destfile="fullpage.html"/>
 * </code>
 * OR
 * <code>
 * <append destfile="${process.outputfile}">
 *    <filterchain>
 *        <xsltfilter style="${process.stylesheet}">
 *            <param name="mode" expression="${process.xslt.mode}"/>
 *            <param name="file_name" expression="%{task.append.current_file.basename}"/> <!-- Example of using a RegisterSlot variable -->
 *        </xsltfilter>
 *    </filterchain>
 *     <filelist dir="book/" listfile="book/PhingGuide.book"/>
 * </append>
 * </code>
 * @package phing.tasks.system
 * @version $Id: d9dc56999ea13b1699b1cc615998eb6c7a795bb4 $
 */
class AppendTask extends Task
{
    /** Append stuff to this file. */
    private $to;

    /** Explicit file to append. */
    private $file;

    /** Any filesets of files that should be appended. */
    private $filesets = array();

    /** Any filters to be applied before append happens. */
    private $filterChains = array();

    /** Text to append. (cannot be used in conjunction w/ files or filesets) */
    private $text;

    private $filtering = true;

    /** @var TextElement $header */
    private $header;

    /** @var TextElement $footer */
    private $footer;

    private $append = true;

    private $fixLastLine = false;

    private $overwrite = true;

    private $eolString;

    private $fileList = array();

    /**
     * @param bool $filtering
     */
    public function setFiltering($filtering)
    {
        $this->filtering = (bool)$filtering;
    }

    /**
     * @param bool $overwrite
     */
    public function setOverwrite($overwrite)
    {
        $this->overwrite = $overwrite;
    }

    /**
     * Set target file to append to.
     *
     * @deprecated Will be removed with final release.
     *
     * @param PhingFile $f
     *
     * @return void
     */
    public function setTo(PhingFile $f)
    {
        $this->log(
            "The 'to' attribute is deprecated in favor of 'destFile'; please update your code.",
            Project::MSG_WARN
        );
        $this->to = $f;
    }

    /**
     * The more conventional naming for method to set destination file.
     *
     * @param PhingFile $f
     *
     * @return void
     */
    public function setDestFile(PhingFile $f)
    {
        $this->to = $f;
    }

    /**
     * Sets the behavior when the destination exists. If set to
     * <code>true</code> the task will append the stream data an
     * {@link Appendable} resource; otherwise existing content will be
     * overwritten. Defaults to <code>false</code>.
     * @param bool $append if true append output.
     */
    public function setAppend($append)
    {
        $this->append = $append;
    }

    /**
     * Specify the end of line to find and to add if
     * not present at end of each input file. This attribute
     * is used in conjunction with fixlastline.
     * @param string $crlf the type of new line to add -
     *              cr, mac, lf, unix, crlf, or dos
     */
    public function setEol($crlf)
    {
        $s = $crlf;
        if ($s === "cr" || $s === "mac") {
            $this->eolString = "\r";
        } elseif ($s === "lf" || $s === "unix") {
            $this->eolString = "\n";
        } elseif ($s === "crlf" || $s === "dos") {
            $this->eolString = "\r\n";
        } else {
            $this->eolString = $this->getProject()->getProperty('line.separator');
        }
    }

    /**
     * Sets specific file to append.
     * @param PhingFile $f
     */
    public function setFile(PhingFile $f)
    {
        $this->file = $f;
    }

    /**
     * Supports embedded <filelist> element.
     *
     * @return FileList
     */
    public function addFileList(FileList $fileList)
    {
        $this->fileList[] = $fileList;
    }

    public function createPath()
    {
        $path = new Path($this->getProject());
        $this->filesets[] = $path;
        return $path;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     *
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Creates a filterchain
     *
     * @return FilterChain The created filterchain object
     */
    public function createFilterChain()
    {
        $num = array_push($this->filterChains, new FilterChain($this->project));

        return $this->filterChains[$num - 1];
    }

    /**
     * Sets text to append.  (cannot be used in conjunction w/ files or filesets).
     *
     * @param string $txt
     *
     * @return void
     */
    public function setText($txt)
    {
        $this->text = (string)$txt;
    }

    /**
     * Sets text to append. Supports CDATA.
     *
     * @param string $txt
     *
     * @return void
     */
    public function addText($txt)
    {
        $this->text .= (string)$txt;
    }

    public function addHeader(TextElement $headerToAdd)
    {
        $this->header = $headerToAdd;
    }

    public function addFooter(TextElement $footerToAdd)
    {
        $this->footer = $footerToAdd;
    }

    /**
     * Append line.separator to files that do not end
     * with a line.separator, default false.
     * @param bool $fixLastLine if true make sure each input file has
     *                          new line on the concatenated stream
     */
    public function setFixLastLine($fixLastLine)
    {
        $this->fixLastLine = $fixLastLine;
    }

    /**
     * Append the file(s).
     *
     * {@inheritdoc}
     */
    public function main()
    {
        $this->validate();

        try {

            if ($this->to !== null) {
                // create a file writer to append to "to" file.
                $writer = new FileWriter($this->to, $this->append);
            } else {
                $writer = new LogWriter($this);
            }

            if ($this->text !== null) {

                // simply append the text
                if ($this->to instanceof PhingFile) {
                    $this->log("Appending string to " . $this->to->getPath());
                }

                $text = $this->text;
                if ($this->filtering) {
                    $fr = $this->getFilteredReader(new StringReader($text));
                    $text = $fr->read();
                }

                $text = $this->appendHeader($text);
                $text = $this->appendFooter($text);
                $writer->write($text);
            } else {

                // append explicitly-specified file
                if ($this->file !== null) {
                    try {
                        $this->appendFile($writer, $this->file);
                    } catch (Exception $ioe) {
                        $this->log(
                            "Unable to append contents of file " . $this->file->getAbsolutePath() . ": " . $ioe->getMessage(),
                            Project::MSG_WARN
                        );
                    }
                }

                // append any files in filesets
                foreach ($this->filesets as $fs) {
                    try {
                        if ($fs instanceof Path) {
                            $files = $fs->listPaths();
                            $this->appendFiles($writer, $files);
                        } elseif ($fs instanceof FileSet) {
                            $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles();
                            $this->appendFiles($writer, $files, $fs->getDir($this->project));
                        }
                    } catch (BuildException $be) {
                        if (strpos($be->getMessage(), 'is the same as the output file') === false) {
                            $this->log($be->getMessage(), Project::MSG_WARN);
                        } else {
                            throw new BuildException($be->getMessage());
                        }
                    } catch (IOException $ioe) {
                        throw new BuildException($ioe);
                    }
                }

                /** @var FileList $list */
                foreach ($this->fileList as $list) {
                    $dir = $list->getDir($this->project);
                    $files = $list->getFiles($this->project);
                    foreach ($files as $file) {
                        $this->appendFile($writer, new PhingFile($dir, $file));
                    }
                }
            }
        } catch (Exception $e) {
            throw new BuildException($e);
        }

        $writer->close();
    }

    private function appendHeader($string)
    {
        $result = $string;
        if ($this->header !== null) {
            $header = $this->header->getValue();
            if ($this->header->filtering) {
                $fr = $this->getFilteredReader(new StringReader($header));
                $header = $fr->read();
            }

            $result = $header . $string;
        }

        return $result;
    }

    private function appendFooter($string)
    {
        $result = $string;
        if ($this->footer !== null) {
            $footer = $this->footer->getValue();
            if ($this->footer->filtering) {
                $fr = $this->getFilteredReader(new StringReader($footer));
                $footer = $fr->read();
            }

            $result = $string . $footer;
        }
        return $result;
    }

    private function validate()
    {
        $this->sanitizeText();

        if ($this->file === null && $this->text === null && count($this->filesets) === 0 && count($this->fileList) === 0) {
            throw new BuildException("You must specify a file, use a filelist/fileset, or specify a text value.");
        }

        if ($this->text !== null && ($this->file !== null || count($this->filesets) > 0)) {
            throw new BuildException("Cannot use text attribute in conjunction with file or fileset");
        }

        if (!$this->eolString) {
            $this->eolString = $this->getProject()->getProperty('line.separator');
        }
    }

    private function sanitizeText()
    {
        if ($this->text !== null && "" === trim($this->text)) {
            $this->text = null;
        }
    }

    private function getFilteredReader(Reader $r)
    {
        $helper = FileUtils::getChainedReader($r, $this->filterChains, $this->getProject());
        return $helper;
    }

    /**
     * Append an array of files in a directory.
     *
     * @param Writer $writer The FileWriter that is appending to target file.
     * @param array $files array of files to delete; can be of zero length
     * @param PhingFile $dir directory to work from
     *
     * @return void
     */
    private function appendFiles(Writer $writer, $files, PhingFile $dir = null)
    {
        if (!empty($files)) {
            $this->log(
                "Attempting to append " . count(
                    $files
                ) . " files" . ($dir !== null ? ", using basedir " . $dir->getPath() : "")
            );
            $basenameSlot = Register::getSlot("task.append.current_file");
            $pathSlot = Register::getSlot("task.append.current_file.path");
            foreach ($files as $file) {
                try {
                    if (!$this->checkFilename($file, $dir)) {
                        continue;
                    }

                    if ($dir !== null) {
                        $file = is_string($file) ? new PhingFile($dir->getPath(), $file) : $file;
                    } else {
                        $file = is_string($file) ? new PhingFile($file) : $file;
                    }
                    $basenameSlot->setValue($file);
                    $pathSlot->setValue($file->getPath());
                    $this->appendFile($writer, $file);
                } catch (IOException $ioe) {
                    $this->log(
                        "Unable to append contents of file " . $file . ": " . $ioe->getMessage(),
                        Project::MSG_WARN
                    );
                } catch (NullPointerException $npe) {
                    $this->log(
                        "Unable to append contents of file " . $file . ": " . $npe->getMessage(),
                        Project::MSG_WARN
                    );
                }
            }
        }
    }

    private function checkFilename($filename, $dir = null)
    {
        if ($dir !== null) {
            $f = new PhingFile($dir, $filename);
        } else {
            $f = new PhingFile($filename);
        }

        if (!$f->exists()) {
            $this->log("File " . (string)$f . " does not exist.", Project::MSG_ERR);
            return false;
        }
        if ($this->to !== null && $f->equals($this->to)) {
            throw new BuildException("Input file \""
                . $f . "\" "
                . "is the same as the output file.");
        }

        if ($this->to !== null
            && !$this->overwrite
            && $this->to->exists()
            && $f->lastModified() > $this->to->lastModified()
        ) {
            $this->log((string)$this->to . " is up-to-date.", Project::MSG_VERBOSE);
            return false;
        }

        return true;
    }

    /**
     * @param FileWriter $writer
     * @param PhingFile $f
     *
     * @return void
     */
    private function appendFile(Writer $writer, PhingFile $f)
    {
        $in = $this->getFilteredReader(new FileReader($f));

        $text = '';
        while (-1 !== ($buffer = $in->read())) { // -1 indicates EOF
            $text .= $buffer;
        }
        if ($this->fixLastLine && ($text[strlen($text) - 1] !== "\n" || $text[strlen($text) - 1] !== "\r")) {
            $text .= $this->eolString;
        }

        $text = $this->appendHeader($text);
        $text = $this->appendFooter($text);
        $writer->write($text);
        if ($f instanceof PhingFile && $this->to instanceof PhingFile) {
            $this->log("Appending contents of " . $f->getPath() . " to " . $this->to->getPath());
        }
    }
}
<?php
/*
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/types/FileList.php';
include_once 'phing/types/FileSet.php';
include_once 'phing/types/DirSet.php';

/**
 * Executes a command on the (filtered) file list/set.
 * (Loosely based on the "Ant Apply" task - http://ant.apache.org/manual/Tasks/apply.html)
 *
 * @author    Utsav Handa <handautsav at hotmail dot com>
 * @package   phing.tasks.system
 *
 * @todo      Add support for mapper, targetfile expressions
 */
class ApplyTask extends Task
{

    /**
     * Configuration(s)
     *
     */
    //[TBA]const TARGETFILE_ID = '__TARGETFILE__';
    const SOURCEFILE_ID = '__SOURCEFILE__';

    /**
     * File Set/List of files.
     * @var array
     */
    protected $filesets = array();
    protected $filelists = array();

    /**
     * Commandline managing object
     * @var commandline
     */
    protected $commandline;

    /**
     * Working directory
     * @var phingfile
     */
    protected $dir;
    protected $currentdirectory;

    /**
     * Command to be executed
     * @var string
     */
    protected $realCommand;

    /**
     * Escape (shell) command using 'escapeshellcmd' before execution
     * @var boolean
     */
    protected $escape = false;

    /**
     * Where to direct output
     * @var phingfile
     */
    protected $output;

    /**
     * Where to direct error
     * @var phingfile
     */
    protected $error;

    /**
     * Whether output should be appended to or overwrite an existing file
     * @var boolean
     */
    protected $appendoutput = false;

    /**
     * Runs the command only once, appending all files as arguments
     * else command will be executed once for every file.
     * @var boolean
     */
    protected $parallel = false;

    /**
     * Whether source file name should be added to the end of command automatically
     * @var boolean
     */
    protected $addsourcefile = true;

    /**
     * Whether to spawn the command execution as a background process
     * @var boolean
     */
    protected $spawn = false;

    /**
     * Property name to set with return value
     * @var string
     */
    protected $returnProperty;

    /**
     * Property name to set with output value
     * @var string
     */
    protected $outputProperty;

    /**
     * Whether the filenames should be passed on the command line as relative pathnames (relative to the base directory of the corresponding fileset/list)
     * @var boolean
     */
    protected $relative = false;

    /**
     * Operating system information
     * @var string
     */
    protected $os;
    protected $currentos;
    protected $osvariant;

    /**
     * Logging level for status messages
     * @var integer
     */
    protected $loglevel = null;

    /**
     * Fail on command that exits with a returncode other than zero
     * @var boolean
     *
     */
    protected $failonerror = false;

    /**
     * Whether to use PHP's passthru() function instead of exec()
     * @var boolean
     */
    protected $passthru = false;


    /**
     * Whether to use forward-slash as file-separator on the file names
     * @var boolean
     */
    protected $forwardslash = false;


    /**
     * Limit the amount of parallelism by passing at most this many sourcefiles at once
     * (Set it to <= 0 for unlimited)
     * @var integer
     */
    protected $maxparallel = 0;


    /**
     * Supports embedded <filelist> element.
     *
     * @return FileList
     */
    public function createFileList()
    {
        $num = array_push($this->filelists, new FileList());

        return $this->filelists[$num - 1];
    }


    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }


    /**
     * Nested adder, adds a set of dirs (nested dirset attribute).
     *
     * @param DirSet $dirSet
     * @return void
     */
    public function addDirSet(DirSet $dirSet)
    {
        $this->filesets[] = $dirSet;
    }


    /**
     * Sets the command executable information
     *
     * @param string $executable Executable path
     *
     * @return void
     */
    public function setExecutable($executable)
    {
        $this->commandline->setExecutable((string) $executable);
    }


    /**
     * Specify the working directory for the command execution.
     *
     * @param PhingFile $dir Set the working directory as specified
     *
     * @return void
     */
    public function setDir(PhingFile $dir)
    {
        $this->dir = $dir;
    }


    /**
     * Escape command using 'escapeshellcmd' before execution
     *
     * @param boolean $escape Escape command before execution
     *
     * @return void
     */
    public function setEscape($escape)
    {
        $this->escape = (bool) $escape;
    }


    /**
     * File to which output should be written
     *
     * @param PhingFile $outputfile Output log file
     *
     * @return void
     */
    public function setOutput(PhingFile $outputfile)
    {
        $this->output = $outputfile;
    }


    /**
     * File to which output should be written
     *
     * @param $append
     * @internal param PhingFile $outputfile Output log file
     *
     * @return void
     */
    public function setAppend($append)
    {
        $this->appendoutput = (bool) $append;
    }


    /**
     * Run the command only once, appending all files as arguments
     *
     * @param Boolean $parallel Identifier for files as arguments appending
     *
     * @return void
     */
    public function setParallel($parallel)
    {
        $this->parallel = (bool) $parallel;
    }


    /**
     * To add the source filename at the end of command of automatically
     *
     * @param Boolean $addsourcefile Identifier for adding source file at the end of command
     *
     * @return void
     */
    public function setAddsourcefile($addsourcefile)
    {
        $this->addsourcefile = (bool) $addsourcefile;
    }


    /**
     * File to which error output should be written
     *
     * @param PhingFile $errorfile Error log file
     *
     * @return void
     */
    public function setError(PhingFile $errorfile)
    {
        $this->error = $errorfile;
    }


    /**
     * Whether to spawn the command and run as background process
     *
     * @param boolean $spawn If the command is to be run as a background process
     *
     * @return void
     */
    public function setSpawn($spawn)
    {
        $this->spawn = (bool) $spawn;
    }


    /**
     * The name of property to set to return value
     *
     * @param string $propertyname Property name
     *
     * @return void
     */
    public function setReturnProperty($propertyname)
    {
        $this->returnProperty = (string) $propertyname;
    }


    /**
     * The name of property to set to output value
     *
     * @param string $propertyname Property name
     *
     * @return void
     */
    public function setOutputProperty($propertyname)
    {
        $this->outputProperty = (string) $propertyname;
    }


    /**
     * Whether the filenames should be passed on the command line as relative
     * pathnames (relative to the base directory of the corresponding fileset/list)
     *
     * @param $relative
     * @internal param bool $escape Escape command before execution
     *
     * @return void
     */
    public function setRelative($relative)
    {
        $this->relative = (bool) $relative;
    }


    /**
     * Specify OS (or multiple OS) that must match in order to execute this command.
     *
     * @param string $os Operating system string (e.g. "Linux")
     *
     * @return void
     */
    public function setOs($os)
    {
        $this->os = (string) $os;
    }


    /**
     * Whether to use PHP's passthru() function instead of exec()
     *
     * @param boolean $passthru If passthru shall be used
     *
     * @return void
     */
    public function setPassthru($passthru)
    {
        $this->passthru = (bool) $passthru;
    }

    /**
     * Fail on command exits with a returncode other than zero
     *
     * @param boolean $failonerror Indicator to fail on error
     *
     * @return void
     */
    public function setFailonerror($failonerror)
    {
        $this->failonerror = (bool) $failonerror;
    }

    /**
     * @param $failonerror
     */
    public function setCheckreturn($failonerror)
    {
        $this->setFailonerror($failonerror);
    }

    /**
     * Whether to use forward-slash as file-separator on the file names
     *
     * @param boolean $forwardslash Indicator to use forward-slash
     *
     * @return void
     */
    public function setForwardslash($forwardslash)
    {
        $this->forwardslash = (bool) $forwardslash;
    }

    /**
     * Limit the amount of parallelism by passing at most this many sourcefiles at once
     *
     * @param $max
     * @internal param bool $forwardslash Indicator to use forward-slash
     *
     * @return void
     */
    public function setMaxparallel($max)
    {
        $this->maxparallel = (int) $max;
    }

    /** [TBA]
     * Supports embedded <targetfile> element.
     *
     * @return void
     */
    /**public function createTargetfile() {
     * return $this->commandline->addArguments( array(self::TARGETFILE_ID) );
     * }*/

    /**
     * Supports embedded <srcfile> element.
     *
     * @return void
     */
    public function createSrcfile()
    {
        return $this->commandline->addArguments(array(self::SOURCEFILE_ID));
    }

    /**
     * Supports embedded <arg> element.
     *
     * @return CommandlineArgument
     */
    public function createArg()
    {
        return $this->commandline->createArgument();
    }

    /**********************************************************************************/
    /**************************** T A S K  M E T H O D S ******************************/
    /**********************************************************************************/

    /**
     * Class Initialization
     * @return void
     */
    public function init()
    {

        $this->commandline = new Commandline();
        $this->loglevel = Project::MSG_VERBOSE;
    }

    /**
     * Do work
     * @throws BuildException
     */
    public function main()
    {

        // Log
        $this->log('Started ', $this->loglevel);

        // Initialize //
        $this->initialize();

        // Validate O.S. applicability
        if ($this->validateOS()) {

            // Build the command //
            $this->buildCommand();

            // Process //
            // - FileLists
            foreach ($this->filelists as $fl) {
                $this->process($fl->getFiles($this->project), $fl->getDir($this->project));
            }
            unset($this->filelists);

            // - FileSets
            foreach ($this->filesets as $fs) {
                $this->process(
                    $fs->getDirectoryScanner($this->project)->getIncludedFiles(),
                    $fs->getDir($this->project)
                );
            }
            unset($this->filesets);

        }

        /// Cleanup //
        $this->cleanup();

        // Log
        $this->log('End ', $this->loglevel);

    }

    /**********************************************************************************/
    /********************** T A S K  C O R E  M E T H O D S ***************************/
    /**********************************************************************************/

    /**
     * Checks whether the current O.S. should be supported
     *
     * @return boolean False if the exec command shall not be run
     */
    protected function validateOS()
    {

        // Log
        $this->log('Validating Operating System information ', $this->loglevel);

        // Checking whether'os' information is specified
        if (empty($this->os)) {

            // Log
            $this->log("Operating system information not specified. Skipped checking. ", $this->loglevel);

            return true;
        }

        // Validating the operating system information
        $matched = (strpos(strtolower($this->os), strtolower($this->currentos)) !== false) ? true : false;

        // Log
        $this->log(
            "Operating system '" . $this->currentos . "' " . ($matched ? '' : 'not ') . "found in " . $this->os,
            $this->loglevel
        );

        return $matched;
    }

    /**
     * Initializes the task operations, i.e.
     * - Required information validation
     * - Working directory
     *
     * @param  none
     *
     * @return void
     */
    private function initialize()
    {

        // Log
        $this->log('Initializing started ', $this->loglevel);

        ///// Validating the required parameters /////

        // Executable
        if ($this->commandline->getExecutable() === null) {
            return $this->throwBuildException('Please provide "executable" information');
        }

        // Retrieving the current working directory
        $this->currentdirectory = getcwd();

        // Directory (in which the command should be executed)
        if ($this->dir !== null) {

            // Try expanding (any) symbolic links
            if (!$this->dir->getCanonicalFile()->isDirectory()) {
                return $this->throwBuildException("'" . $this->dir . "' is not a valid directory");
            }

            // Change working directory
            $dirchangestatus = @chdir($this->dir->getPath());

            // Log
            $this->log(
                'Working directory change ' . ($dirchangestatus ? 'successful' : 'failed') . ' to ' . $this->dir->getPath(
                ),
                $this->loglevel
            );

        }

        ///// Preparing the task environment /////

        // Getting current operationg system
        $this->currentos = Phing::getProperty('os.name');

        // Log
        $this->log('Operating System identified : ' . $this->currentos, $this->loglevel);

        // Getting the O.S. type identifier
        // Validating the 'filesystem' for determining the OS type [UNIX, WINNT and WIN32]
        // (Another usage could be with 'os.name' for determination)
        if ('WIN' == strtoupper(substr(Phing::getProperty('host.fstype'), 0, 3))) {
            $this->osvariant = 'WIN'; // Probable Windows flavour
        } else {
            $this->osvariant = 'LIN'; // Probable GNU/Linux flavour
        }

        // Log
        $this->log('Operating System variant identified : ' . $this->osvariant, $this->loglevel);

        // Log
        $this->log('Initializing completed ', $this->loglevel);

        return;
    }

    /**
     * Builds the full command to execute and stores it in $realCommand.
     *
     * @return void
     */
    private function buildCommand()
    {

        // Log
        $this->log('Command building started ', $this->loglevel);

        // Building the executable
        $this->realCommand = Commandline::toString($this->commandline->getCommandline(), $this->escape);

        // Adding the source filename at the end of command, validating the existing
        // sourcefile position explicit mentioning
        if (($this->addsourcefile === true) && (strpos($this->realCommand, self::SOURCEFILE_ID) === false)) {
            $this->realCommand .= ' ' . self::SOURCEFILE_ID;
        }

        // Setting command output redirection with content appending
        if ($this->output !== null) {

            $this->realCommand .= ' 1>';
            $this->realCommand .= ($this->appendoutput ? '>' : ''); // Append output
            $this->realCommand .= ' ' . escapeshellarg($this->output->getPath());

        } elseif ($this->spawn) { // Validating the 'spawn' configuration, and redirecting the output to 'null'

            // Validating the O.S. variant
            if ('WIN' == $this->osvariant) {
                $this->realCommand .= ' > NUL'; // MS Windows output nullification
            } else {
                $this->realCommand .= ' 1>/dev/null'; // GNU/Linux output nullification
            }

            $this->log("For process spawning, setting Output nullification ", $this->loglevel);
        }

        // Setting command error redirection with content appending
        if ($this->error !== null) {
            $this->realCommand .= ' 2>';
            $this->realCommand .= ($this->appendoutput ? '>' : ''); // Append error
            $this->realCommand .= ' ' . escapeshellarg($this->error->getPath());
        }

        // Setting the execution as a background process
        if ($this->spawn) {

            // Validating the O.S. variant
            if ('WIN' == $this->osvariant) {
                $this->realCommand = 'start /b ' . $this->realCommand; // MS Windows background process forking
            } else {
                $this->realCommand .= ' &'; // GNU/Linux background process forking
            }

        }

        // Log
        $this->log('Command built : ' . $this->realCommand, $this->loglevel);

        // Log
        $this->log('Command building completed ', $this->loglevel);

        return;
    }

    /**
     * Processes the files list with provided information for execution
     *
     * @param array $files File list for processing
     * @param string $basedir Base directory of the file list
     *
     * @return void
     */
    private function process($files, $basedir)
    {

        // Log
        $this->log("Processing Filelist with base directory ($basedir) ", $this->loglevel);

        // Process each file in the list for applying the 'realcommand'
        foreach ($files as $count => $file) {

            // Preparing the absolute filename with relative path information
            $absolutefilename = $this->getFilePath($file, $basedir, $this->relative);

            // Checking whether 'parallel' information is enabled. If enabled, append all
            // the file names as arguments, and run only once.
            if ($this->parallel) {

                // Checking whether 'maxparallel' setting describes parallelism limitation
                // by passing at most 'maxparallel' many sourcefiles at once
                $slicedfiles = array_splice(
                    $files,
                    0,
                    (($this->maxparallel > 0) ? $this->maxparallel : count($files))
                );;

                $absolutefilename = implode(' ', $this->getFilePath($slicedfiles, $basedir, $this->relative));
            }

            // Checking whether the forward-slash as file-separator has been set.
            // (Applicability: The source {and target} file names must use the forward slash as file separator)
            if ($this->forwardslash) {
                $absolutefilename = str_replace(DIRECTORY_SEPARATOR, '/', $absolutefilename);
            }

            // Preparing the command to be executed
            $filecommand = str_replace(array(self::SOURCEFILE_ID), array($absolutefilename), $this->realCommand);

            // Command execution
            list($returncode, $output) = $this->executeCommand($filecommand);

            // Process the stuff on the first command execution only
            if (0 == $count) {

                // Sets the return property
                if ($this->returnProperty) {
                    $this->project->setProperty($this->returnProperty, $returncode);
                }

            }

            // Sets the output property
            if ($this->outputProperty) {
                $previousValue = $this->project->getProperty($this->outputProperty);
                if (! empty($previousValue)) {
                    $previousValue .= "\n";
                }
                $this->project->setProperty($this->outputProperty, $previousValue . implode("\n", $output));
            }

            // Validating the 'return-code'
            if (($this->failonerror) && ($returncode != 0)) {
                $this->throwBuildException("Task exited with code ($returncode)");
            }

            // Validate the 'parallel' information for command execution. If the command has been
            // executed with the filenames as argument, considering 'maxparallel', just break.
            if (($this->parallel) && (!array_key_exists($count, $files))) {
                break;
            }

        } // Each file processing loop ends

        return;
    }

    /**
     * Executes the specified command and returns the return code & output.
     *
     * @param string $command
     *
     * @return array array(return code, array with output)
     */
    private function executeCommand($command)
    {

        // Var(s)
        $output = array();
        $return = null;

        // Validating the command executor container
        ($this->passthru ? passthru($command, $return) : exec($command, $output, $return));

        // Log
        $this->log(
            'Command execution : (' . ($this->passthru ? 'passthru' : 'exec') . ') : ' . $command . " : completed with return code ($return) ",
            $this->loglevel
        );

        return array($return, $output);
    }

    /**
     * Runs cleanup tasks post execution
     * - Restore working directory
     *
     * @return void
     */
    private function cleanup()
    {

        // Restore working directory
        if ($this->dir !== null) {
            @chdir($this->currentdirectory);
        }

        return;
    }

    /**
     * Prepares the filename per base directory and relative path information
     *
     * @param $filename
     * @param $basedir
     * @param $relative
     *
     * @return mixed processed filenames
     */
    public function getFilePath($filename, $basedir, $relative)
    {

        // Var(s)
        $files = array();

        // Validating the 'file' information
        $files = (is_array($filename)) ? $filename : array($filename);

        // Processing the file information
        foreach ($files as $index => $file) {
            $absolutefilename = (($relative === false) ? ($basedir . DIRECTORY_SEPARATOR) : '');
            $absolutefilename .= $file;
            $files[$index] = $absolutefilename;
        }

        return (is_array($filename) ? $files : $files[0]);
    }

    /**
     * Throws the exception with specified information
     *
     * @param  $information Exception information
     *
     * @throws BuildException
     * @return void
     */
    private function throwBuildException($information)
    {
        throw new BuildException('ApplyTask: ' . (string) $information);
    }

}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/tasks/system/ApplyTask.php';
include_once 'phing/Phing.php';
include_once 'phing/Project.php';
include_once 'phing/BuildException.php';
include_once 'phing/system/io/PhingFile.php';
include_once 'phing/util/StringHelper.php';

/**
 * Changes the attributes of a file or all files inside specified directories.
 * Right now it has effect only under Windows. Each of the 4 possible
 * permissions has its own attribute, matching the arguments for the `attrib`
 * command.
 *
 * Example:
 * ```
 *    <attrib file="${input}" readonly="true" hidden="true" verbose="true"/>
 * ```
 *
 * @author  Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.tasks.system
 */
class AttribTask extends ApplyTask
{
    private static $ATTR_READONLY = 'R';
    private static $ATTR_ARCHIVE = 'A';
    private static $ATTR_SYSTEM = 'S';
    private static $ATTR_HIDDEN = 'H';
    private static $SET = '+';
    private static $UNSET = '-';

    private $attr = false;

    public function init()
    {
        parent::init();
        parent::setExecutable('attrib');
        parent::setParallel(false);
    }

    /**
     * @throws BuildException
     */
    public function main()
    {
        $this->checkConfiguration();
        parent::main();
    }

    /**
     * @param bool $b
     */
    public function setVerbose($b)
    {
        $this->logLevel = Project::MSG_VERBOSE;
    }

    /**
     * A file to be attribed.
     * @param PhingFile $src a file
     */
    public function setFile(PhingFile $src)
    {
        $fs = new FileSet();
        $fs->setFile($src);
        $this->addFileSet($fs);
    }

    /**
     * Set the ReadOnly file attribute.
     * @param boolean $value
     */
    public function setReadonly($value)
    {
        $this->addArg($value, self::$ATTR_READONLY);
    }

    /**
     * Set the Archive file attribute.
     * @param boolean $value
     */
    public function setArchive($value)
    {
        $this->addArg($value, self::$ATTR_ARCHIVE);
    }

    /**
     * Set the System file attribute.
     * @param boolean $value
     */
    public function setSystem($value)
    {
        $this->addArg($value, self::$ATTR_SYSTEM);
    }

    /**
     * Set the Hidden file attribute.
     * @param boolean $value
     */
    public function setHidden($value)
    {
        $this->addArg($value, self::$ATTR_HIDDEN);
    }

    /**
     * Check the attributes.
     * @throws BuildException
     */
    protected function checkConfiguration()
    {
        if (!$this->hasAttr()) {
            throw new BuildException(
                'Missing attribute parameter',
                $this->getLocation()
            );
        }
    }

    /**
     * Set the executable.
     * This is not allowed, and it always throws a BuildException.
     * @param mixed $e
     * @throws BuildException
     */
    public function setExecutable($e)
    {
        throw new BuildException(
            $this->getTaskType() . ' doesn\'t support the executable attribute',
            $this->getLocation()
        );
    }

    /**
     * Add source file.
     * This is not allowed, and it always throws a BuildException.
     * @param boolean $b ignored
     * @throws BuildException
     */
    public function setAddsourcefile($b)
    {
        throw new BuildException(
            $this->getTaskType()
                . ' doesn\'t support the addsourcefile attribute',
            $this->getLocation()
        );
    }

    /**
     * Set max parallel.
     * This is not allowed, and it always throws a BuildException.
     * @param int $max ignored
     * @throws BuildException
     */
    public function setMaxParallel($max)
    {
        throw new BuildException(
            $this->getTaskType()
                . ' doesn\'t support the maxparallel attribute',
            $this->getLocation()
        );
    }

    /**
     * Set parallel.
     * This is not allowed, and it always throws a BuildException.
     * @param boolean $parallel ignored
     * @throws BuildException
     */
    public function setParallel($parallel)
    {
        throw new BuildException(
            $this->getTaskType()
            . ' doesn\'t support the parallel attribute',
            $this->getLocation()
        );
    }

    protected function validateOS()
    {
        return $this->os === null && $this->osvariant === null
            ? $this->os === null && $this->osvariant === null
            : parent::validateOS();
    }

    private static function getSignString($attr)
    {
        return ($attr ? self::$SET : self::$UNSET);
    }

    private function addArg($sign, $attribute)
    {
        $this->createArg()->setValue(self::getSignString($sign) . $attribute);
        $this->attr = true;
    }

    /**
     * @return bool
     */
    private function hasAttr()
    {
        return $this->attr;
    }
}
<?php
/*
 *  $Id: f41bd7e5c2664f1f58cf5c13d30b8ea2be1fa5e4 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/system/io/FileSystem.php';
include_once 'phing/tasks/system/condition/Condition.php';

/**
 * <available> task.
 *
 * Note: implements condition interface (see condition/Condition.php)
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: f41bd7e5c2664f1f58cf5c13d30b8ea2be1fa5e4 $
 * @package   phing.tasks.system
 */
class AvailableTask extends Task implements Condition
{

    /** Property to check for. */
    private $property;

    /** Value property should be set to. */
    private $value = "true";

    /** File/directory to check existence */
    private $file;

    /** Resource to check for */
    private $resource;

    /** Extension to check if is loaded */
    private $extension;

    private $type = null;
    private $filepath = null;

    private $followSymlinks = false;

    /**
     * @param $property
     */
    public function setProperty($property)
    {
        $this->property = (string) $property;
    }

    /**
     * @param $value
     */
    public function setValue($value)
    {
        $this->value = (string) $value;
    }

    /**
     * @param PhingFile $file
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * @param $resource
     */
    public function setResource($resource)
    {
        $this->resource = (string) $resource;
    }

    /**
     * @param $extension
     */
    public function setExtension($extension)
    {
        $this->extension = (string) $extension;
    }

    /**
     * @param $type
     */
    public function setType($type)
    {
        $this->type = (string) strtolower($type);
    }

    /**
     * @param $followSymlinks
     */
    public function setFollowSymlinks($followSymlinks)
    {
        $this->followSymlinks = (bool) $followSymlinks;
    }

    /**
     * Set the path to use when looking for a file.
     *
     * @param Path $filepath a Path instance containing the search path for files.
     */
    public function setFilepath(Path $filepath)
    {
        if ($this->filepath === null) {
            $this->filepath = $filepath;
        } else {
            $this->filepath->append($filepath);
        }
    }

    /**
     * Creates a path to be configured
     *
     * @return Path
     */
    public function createFilepath()
    {
        if ($this->filepath === null) {
            $this->filepath = new Path($this->project);
        }

        return $this->filepath->createPath();
    }

    public function main()
    {
        if ($this->property === null) {
            throw new BuildException("property attribute is required", $this->location);
        }
        if ($this->evaluate()) {
            $this->project->setProperty($this->property, $this->value);
        }
    }

    /**
     * @return bool
     * @throws BuildException
     */
    public function evaluate()
    {
        if ($this->file === null && $this->resource === null && $this->extension === null) {
            throw new BuildException("At least one of (file|resource|extension) is required", $this->location);
        }

        if ($this->type !== null && ($this->type !== "file" && $this->type !== "dir")) {
            throw new BuildException("Type must be one of either dir or file", $this->location);
        }

        if (($this->file !== null) && !$this->_checkFile()) {
            $this->log(
                "Unable to find " . $this->file->__toString() . " to set property " . $this->property,
                Project::MSG_VERBOSE
            );

            return false;
        }

        if (($this->resource !== null) && !$this->_checkResource($this->resource)) {
            $this->log(
                "Unable to load resource " . $this->resource . " to set property " . $this->property,
                Project::MSG_VERBOSE
            );

            return false;
        }

        if ($this->extension !== null && !extension_loaded($this->extension)) {
            $this->log(
                "Unable to load extension " . $this->extension . " to set property " . $this->property,
                Project::MSG_VERBOSE
            );

            return false;
        }

        return true;
    }

    // this is prepared for the path type
    /**
     * @return bool
     */
    private function _checkFile()
    {
        if ($this->filepath === null) {
            return $this->_checkFile1($this->file);
        } else {
            $paths = $this->filepath->listPaths();
            foreach ($paths as $path) {
                $this->log("Searching " . $path, Project::MSG_VERBOSE);
                $tmp = new PhingFile($path, $this->file->getName());
                if ($tmp->isFile()) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * @param PhingFile $file
     * @return bool
     * @throws IOException
     */
    private function _checkFile1(PhingFile $file)
    {
        // Resolve symbolic links
        if ($this->followSymlinks && $file->isLink()) {
            $linkTarget = new PhingFile($file->getLinkTarget());
            if ($linkTarget->isAbsolute()) {
                $file = $linkTarget;
            } else {
                $fs = FileSystem::getFileSystem();
                $file = new PhingFile(
                    $fs->resolve(
                        $fs->normalize($file->getParent()),
                        $fs->normalize($file->getLinkTarget())
                    )
                );
            }
        }

        if ($this->type !== null) {
            if ($this->type === "dir") {
                return $file->isDirectory();
            } else {
                if ($this->type === "file") {
                    return $file->isFile();
                }
            }
        }

        return $file->exists();
    }

    /**
     * @param $resource
     * @return bool
     */
    private function _checkResource($resource)
    {
        if (null != ($resourcePath = Phing::getResourcePath($resource))) {
            return $this->_checkFile1(new PhingFile($resourcePath));
        } else {
            return false;
        }
    }
}
<?php
/**
 *  $Id: c96657cf752403a437366423508e8df77b743109 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/BuildException.php';
include_once 'phing/Task.php';
include_once 'phing/system/io/PhingFile.php';
include_once 'phing/util/StringHelper.php';

/**
 * Task that changes the permissions on a file/directory.
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing.tasks.system
 */
class Basename extends Task
{
    /** @var PhingFile $file */
    private $file;

    /** @var string $property */
    private $property;

    /** @var string $suffix */
    private $suffix;

    /**
     * file or directory to get base name from
     * @param PhingFile $file file or directory to get base name from
     */
    public function setFile($file)
    {
        if (is_string($file)) {
            $this->file = new PhingFile($file);
        } else {
            $this->file = $file;
        }
    }

    /**
     * Property to set base name to.
     * @param string $property name of property
     */
    public function setProperty($property)
    {
        $this->property = $property;
    }

    /**
     * Optional suffix to remove from base name.
     * @param string $suffix suffix to remove from base name
     */
    public function setSuffix($suffix)
    {
        $this->suffix = $suffix;
    }

    /**
     * do the work
     * @throws BuildException if required attributes are not supplied
     *                        property and attribute are required attributes
     */
    public function main()
    {
        if ($this->property === null) {
            throw new BuildException("property attribute required", $this->getLocation());
        }

        if ($this->file == null) {
            throw new BuildException("file attribute required", $this->getLocation());
        }

        $value = $this->file->getName();
        if ($this->suffix != null && StringHelper::endsWith($this->suffix, $value)) {
            // if the suffix does not starts with a '.' and the
            // char preceding the suffix is a '.', we assume the user
            // wants to remove the '.' as well
            $pos = strlen($value) - strlen($this->suffix) - 1;
            if ($pos > 0 && $this->suffix{0} !== '.' && $value{$pos} === '.') {
                $pos--;
            }
            $value = StringHelper::substring($value, 0, $pos);

        }
        $this->getProject()->setNewProperty($this->property, $value);
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */
require_once 'phing/Task.php';
require_once 'phing/BuildTimeoutException.php';
require_once 'phing/tasks/system/WaitForTask.php';

/**
 *  Based on Apache Ant Block For:
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing.tasks.system
 */
class BlockForTask extends WaitForTask
{
    /**
     * Text to include in a message
     */
    private $text;

    public function __construct($taskName = 'blockfor')
    {
        parent::__construct($taskName);
    }

    /**
     * If the wait fails, a BuildException is thrown. All the superclasses actions are called first.
     * @throws BuildTimeoutException on timeout, using the text in {@link #text}
     *
     */
    protected function processTimeout()
    {
        parent::processTimeout();
        throw new BuildTimeoutException($this->text);
    }

    /**
     * Set the error text; all properties are expanded in the message.
     *
     * @param string $message the text to use in a failure message
     */
    public function addText($message)
    {
        $this->text = $this->getProject()->replaceProperties($message);
    }
}
<?php
/*
 *  $Id: 0bc1e0d42c4f6bf5d80bdf04991b8d12c27bf107 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/types/FileSet.php';

/**
 * Task that changes the permissions on a file/directory.
 *
 * @author    Manuel Holtgrewe <grin@gmx.net>
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: 0bc1e0d42c4f6bf5d80bdf04991b8d12c27bf107 $
 * @package   phing.tasks.system
 */
class ChmodTask extends Task
{

    private $file;

    private $mode;

    private $filesets = array();

    private $filesystem;

    private $quiet = false;
    private $failonerror = true;
    private $verbose = true;

    /**
     * This flag means 'note errors to the output, but keep going'
     * @see setQuiet()
     * @param $bool
     */
    public function setFailonerror($bool)
    {
        $this->failonerror = $bool;
    }

    /**
     * Set quiet mode, which suppresses warnings if chmod() fails.
     * @see setFailonerror()
     * @param $bool
     */
    public function setQuiet($bool)
    {
        $this->quiet = $bool;
        if ($this->quiet) {
            $this->failonerror = false;
        }
    }

    /**
     * Set verbosity, which if set to false surpresses all but an overview
     * of what happened.
     * @param $bool
     */
    public function setVerbose($bool)
    {
        $this->verbose = (bool) $bool;
    }

    /**
     * Sets a single source file to touch.  If the file does not exist
     * an empty file will be created.
     * @param PhingFile $file
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * @param $str
     */
    public function setMode($str)
    {
        $this->mode = $str;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Execute the touch operation.
     * @return void
     */
    public function main()
    {
        // Check Parameters
        $this->checkParams();
        $this->chmod();
    }

    /**
     * Ensure that correct parameters were passed in.
     * @throws BuildException
     * @return void
     */
    private function checkParams()
    {

        if ($this->file === null && empty($this->filesets)) {
            throw new BuildException("Specify at least one source - a file or a fileset.");
        }

        if ($this->mode === null) {
            throw new BuildException("You have to specify an octal mode for chmod.");
        }

        // check for mode to be in the correct format
        if (!preg_match('/^([0-7]){3,4}$/', $this->mode)) {
            throw new BuildException("You have specified an invalid mode.");
        }

    }

    /**
     * Does the actual work.
     * @return void
     */
    private function chmod()
    {

        if (strlen($this->mode) === 4) {
            $mode = octdec($this->mode);
        } else {
            // we need to prepend the 0 before converting
            $mode = octdec("0" . $this->mode);
        }

        // counters for non-verbose output
        $total_files = 0;
        $total_dirs = 0;

        // one file
        if ($this->file !== null) {
            $total_files = 1;
            $this->chmodFile($this->file, $mode);
        }

        // filesets
        foreach ($this->filesets as $fs) {

            $ds = $fs->getDirectoryScanner($this->project);
            $fromDir = $fs->getDir($this->project);

            $srcFiles = $ds->getIncludedFiles();
            $srcDirs = $ds->getIncludedDirectories();

            $filecount = count($srcFiles);
            $total_files = $total_files + $filecount;
            for ($j = 0; $j < $filecount; $j++) {
                $this->chmodFile(new PhingFile($fromDir, $srcFiles[$j]), $mode);
            }

            $dircount = count($srcDirs);
            $total_dirs = $total_dirs + $dircount;
            for ($j = 0; $j < $dircount; $j++) {
                $this->chmodFile(new PhingFile($fromDir, $srcDirs[$j]), $mode);
            }
        }

        if (!$this->verbose) {
            $this->log('Total files changed to ' . vsprintf('%o', $mode) . ': ' . $total_files);
            $this->log('Total directories changed to ' . vsprintf('%o', $mode) . ': ' . $total_dirs);
        }

    }

    /**
     * Actually change the mode for the file.
     * @param PhingFile $file
     * @param int $mode
     * @throws BuildException
     * @throws Exception
     */
    private function chmodFile(PhingFile $file, $mode)
    {
        if (!$file->exists()) {
            throw new BuildException("The file " . $file->__toString() . " does not exist");
        }

        try {
            $file->setMode($mode);
            if ($this->verbose) {
                $this->log("Changed file mode on '" . $file->__toString() . "' to " . vsprintf("%o", $mode));
            }
        } catch (Exception $e) {
            if ($this->failonerror) {
                throw $e;
            } else {
                $this->log($e->getMessage(), $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
            }
        }
    }

}
<?php
/*
 *  $Id: 211fc3c1da06e93f652f487022ec9e39941c40ed $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/types/FileSet.php';

/**
 * Task that changes the permissions on a file/directory.
 *
 * @author    Mehmet Emre Yilmaz <mehmety@gmail.com>
 * @version   $Id: 211fc3c1da06e93f652f487022ec9e39941c40ed $
 * @package   phing.tasks.system
 */
class ChownTask extends Task
{

    private $file;

    private $user;
    private $group;

    private $filesets = array();

    private $filesystem;

    private $quiet = false;
    private $failonerror = true;
    private $verbose = true;

    /**
     * This flag means 'note errors to the output, but keep going'
     * @see setQuiet()
     * @param $bool
     */
    public function setFailonerror($bool)
    {
        $this->failonerror = $bool;
    }

    /**
     * Set quiet mode, which suppresses warnings if chown() fails.
     * @see setFailonerror()
     * @param $bool
     */
    public function setQuiet($bool)
    {
        $this->quiet = $bool;
        if ($this->quiet) {
            $this->failonerror = false;
        }
    }

    /**
     * Set verbosity, which if set to false surpresses all but an overview
     * of what happened.
     * @param $bool
     */
    public function setVerbose($bool)
    {
        $this->verbose = (bool) $bool;
    }

    /**
     * Sets a single source file to touch.  If the file does not exist
     * an empty file will be created.
     * @param PhingFile $file
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * Sets the user
     * @param $user
     */
    public function setUser($user)
    {
        $this->user = $user;
    }

    /**
     * Sets the group
     * @param $group
     */
    public function setGroup($group)
    {
        $this->group = $group;
    }

    /**
     * Nested creator, adds a set of files (nested fileset attribute).
     * @param FileSet $fs
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Execute the touch operation.
     * @return void
     */
    public function main()
    {
        // Check Parameters
        $this->checkParams();
        $this->chown();
    }

    /**
     * Ensure that correct parameters were passed in.
     * @throws BuildException
     * @return void
     */
    private function checkParams()
    {

        if ($this->file === null && empty($this->filesets)) {
            throw new BuildException("Specify at least one source - a file or a fileset.");
        }

        if ($this->user === null && $this->group === null) {
            throw new BuildException("You have to specify either an owner or a group for chown.");
        }
    }

    /**
     * Does the actual work.
     * @return void
     */
    private function chown()
    {
        $userElements = explode('.', $this->user);

        $user = $userElements[0];

        if (count($userElements) > 1) {
            $group = $userElements[1];
        } else {
            $group = $this->group;
        }

        // counters for non-verbose output
        $total_files = 0;
        $total_dirs = 0;

        // one file
        if ($this->file !== null) {
            $total_files = 1;
            $this->chownFile($this->file, $user, $group);
        }

        // filesets
        foreach ($this->filesets as $fs) {

            $ds = $fs->getDirectoryScanner($this->project);
            $fromDir = $fs->getDir($this->project);

            $srcFiles = $ds->getIncludedFiles();
            $srcDirs = $ds->getIncludedDirectories();

            $filecount = count($srcFiles);
            $total_files = $total_files + $filecount;
            for ($j = 0; $j < $filecount; $j++) {
                $this->chownFile(new PhingFile($fromDir, $srcFiles[$j]), $user, $group);
            }

            $dircount = count($srcDirs);
            $total_dirs = $total_dirs + $dircount;
            for ($j = 0; $j < $dircount; $j++) {
                $this->chownFile(new PhingFile($fromDir, $srcDirs[$j]), $user, $group);
            }
        }

        if (!$this->verbose) {
            $this->log('Total files changed to ' . $user . ($group ? "." . $group : "") . ': ' . $total_files);
            $this->log('Total directories changed to ' . $user . ($group ? "." . $group : "") . ': ' . $total_dirs);
        }

    }

    /**
     * Actually change the mode for the file.
     * @param PhingFile $file
     * @param string $user
     * @param string $group
     * @throws BuildException
     * @throws Exception
     */
    private function chownFile(PhingFile $file, $user, $group = "")
    {
        if (!$file->exists()) {
            throw new BuildException("The file " . $file->__toString() . " does not exist");
        }

        try {
            if (!empty($user)) {
                $file->setUser($user);
            }

            if (!empty($group)) {
                $file->setGroup($group);
            }

            if ($this->verbose) {
                $this->log(
                    "Changed file owner on '" . $file->__toString() . "' to " . $user . ($group ? "." . $group : "")
                );
            }
        } catch (Exception $e) {
            if ($this->failonerror) {
                throw $e;
            } else {
                $this->log($e->getMessage(), $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
            }
        }
    }

}
<?php
/*
 * $Id: 8449167915cfddb5dead1d8e7535c49920f792c5 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/condition/ConditionBase.php';

/**
 * <and> condition container.
 *
 * Iterates over all conditions and returns false as soon as one
 * evaluates to false.
 *
 * @author    Hans Lellelid <hans@xmpl.org>
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: 8449167915cfddb5dead1d8e7535c49920f792c5 $
 * @package   phing.tasks.system.condition
 */
class AndCondition extends ConditionBase implements Condition
{

    /**
     * @return bool
     */
    public function evaluate()
    {
        foreach ($this as $c) { // ConditionBase implements IteratorAggregator
            if (!$c->evaluate()) {
                return false;
            }
        }

        return true;
    }
}
<?php

/*
 *  $Id: 746f4adc744f89eadf0ccdea8c137cbf5f5614b9 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Condition interface specification:
 *
 * Each condition must implement a method applying to this prototye:
 *
 * @author Hans Lellelid <hans@xmpl.org>
 * @version $Id: 746f4adc744f89eadf0ccdea8c137cbf5f5614b9 $
 * @package phing.tasks.system.condition
 */
interface Condition
{
    /**
     * @return boolean
     * @throws BuildException
     */
    public function evaluate();
}
<?php
/*
 *  $Id: dc5d657411378951be19ff5d321a9442afc1a70c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/ProjectComponent.php';
include_once 'phing/Project.php';
include_once 'phing/tasks/system/AvailableTask.php';
include_once 'phing/tasks/system/condition/Condition.php';
include_once 'phing/parser/CustomChildCreator.php';

/**
 * Abstract baseclass for the <condition> task as well as several
 * conditions - ensures that the types of conditions inside the task
 * and the "container" conditions are in sync.
 *
 * @author  Hans Lellelid <hans@xmpl.org>
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: dc5d657411378951be19ff5d321a9442afc1a70c $
 * @package   phing.tasks.system.condition
 */
abstract class ConditionBase extends ProjectComponent
    implements IteratorAggregate, CustomChildCreator
{

    public $conditions = array(); // needs to be public for "inner" class access

    /** @var string $taskName */
    private $taskName = 'condition';

    public function __construct($taskName = 'component')
    {
        $this->taskName = $taskName;
    }

    /**
     * Sets the name to use in logging messages.
     *
     * @param string $name The name to use in logging messages.
     *                     Should not be <code>null</code>.
     */
    public function setTaskName($name)
    {
        $this->taskName = $name;
    }

    /**
     * Returns the name to use in logging messages.
     *
     * @return string the name to use in logging messages.
     */
    public function getTaskName()
    {
        return $this->taskName;
    }

    /**
     * @return int
     */
    public function countConditions()
    {
        return count($this->conditions);
    }

    /**
     * Required for IteratorAggregate
     */
    public function getIterator()
    {
        return new ConditionEnumeration($this);
    }

    /**
     * @return Condition[]
     */
    public function getConditions()
    {
        return $this->conditions;
    }

    /**
     * @param AvailableTask $a
     * @return void
     */
    public function addAvailable(AvailableTask $a)
    {
        $this->conditions[] = $a;
    }

    /**
     * @return NotCondition
     */
    public function createNot()
    {
        include_once 'phing/tasks/system/condition/NotCondition.php';
        $num = array_push($this->conditions, new NotCondition());

        return $this->conditions[$num - 1];
    }

    /**
     * @return AndCondition
     */
    public function createAnd()
    {
        include_once 'phing/tasks/system/condition/AndCondition.php';
        $num = array_push($this->conditions, new AndCondition());

        return $this->conditions[$num - 1];
    }

    /**
     * @return OrCondition
     */
    public function createOr()
    {
        include_once 'phing/tasks/system/condition/OrCondition.php';
        $num = array_push($this->conditions, new OrCondition());

        return $this->conditions[$num - 1];
    }

    /**
     * @return XorCondition
     */
    public function createXor()
    {
        include_once 'phing/tasks/system/condition/XorCondition.php';
        $num = array_push($this->conditions, new XorCondition());

        return $this->conditions[$num - 1];
    }

    /**
     * @return EqualsCondition
     */
    public function createEquals()
    {
        include_once 'phing/tasks/system/condition/EqualsCondition.php';
        $num = array_push($this->conditions, new EqualsCondition());

        return $this->conditions[$num - 1];
    }

    /**
     * @return OsCondition
     */
    public function createOs()
    {
        include_once 'phing/tasks/system/condition/OsCondition.php';
        $num = array_push($this->conditions, new OsCondition());

        return $this->conditions[$num - 1];
    }

    /**
     * @return IsFalseCondition
     */
    public function createIsFalse()
    {
        include_once 'phing/tasks/system/condition/IsFalseCondition.php';
        $num = array_push($this->conditions, new IsFalseCondition());

        return $this->conditions[$num - 1];
    }

    /**
     * @return IsTrueCondition
     */
    public function createIsTrue()
    {
        include_once 'phing/tasks/system/condition/IsTrueCondition.php';
        $num = array_push($this->conditions, new IsTrueCondition());

        return $this->conditions[$num - 1];
    }

    /**
     * @return IsPropertyFalseCondition
     */
    public function createIsPropertyFalse()
    {
        include_once 'phing/tasks/system/condition/IsPropertyFalseCondition.php';
        $num = array_push($this->conditions, new IsPropertyFalseCondition());

        return $this->conditions[$num - 1];
    }

    /**
     * @return IsPropertyTrueCondition
     */
    public function createIsPropertyTrue()
    {
        include_once 'phing/tasks/system/condition/IsPropertyTrueCondition.php';
        $num = array_push($this->conditions, new IsPropertyTrueCondition());

        return $this->conditions[$num - 1];
    }

    /**
     * @return ContainsCondition
     */
    public function createContains()
    {
        include_once 'phing/tasks/system/condition/ContainsCondition.php';
        $num = array_push($this->conditions, new ContainsCondition());

        return $this->conditions[$num - 1];
    }

    /**
     * @return IsSetCondition
     */
    public function createIsSet()
    {
        include_once 'phing/tasks/system/condition/IsSetCondition.php';
        $num = array_push($this->conditions, new IsSetCondition());

        return $this->conditions[$num - 1];
    }

    /**
     * @return ReferenceExistsCondition
     */
    public function createReferenceExists()
    {
        include_once 'phing/tasks/system/condition/ReferenceExistsCondition.php';
        $num = array_push($this->conditions, new ReferenceExistsCondition());

        return $this->conditions[$num - 1];
    }
    
    public function createVersionCompare()
    {
        include_once 'phing/tasks/system/condition/VersionCompareCondition.php';
        $num = array_push($this->conditions, new VersionCompareCondition());

        return $this->conditions[$num - 1];
    }

    public function createHttp()
    {
        include_once 'phing/tasks/system/condition/HttpCondition.php';
        $num = array_push($this->conditions, new HttpCondition());

        return $this->conditions[$num - 1];
    }

    public function createPhingVersion()
    {
        include_once 'phing/tasks/system/condition/PhingVersion.php';
        $num = array_push($this->conditions, new PhingVersion());

        return $this->conditions[$num - 1];
    }

    public function createHasFreeSpace()
    {
        include_once 'phing/tasks/system/condition/HasFreeSpaceCondition.php';
        $num = array_push($this->conditions, new HasFreeSpaceCondition());

        return $this->conditions[$num - 1];
    }

    public function createFilesMatch()
    {
        include_once 'phing/tasks/system/condition/FilesMatch.php';
        $num = array_push($this->conditions, new FilesMatch());

        return $this->conditions[$num - 1];
    }

    public function createSocket()
    {
        include_once 'phing/tasks/system/condition/SocketCondition.php';
        $num = array_push($this->conditions, new SocketCondition());

        return $this->conditions[$num - 1];
    }

    public function createIsFailure()
    {
        include_once 'phing/tasks/system/condition/IsFailure.php';
        $num = array_push($this->conditions, new IsFailure());

        return $this->conditions[$num - 1];
    }

    public function createIsFileSelected()
    {
        include_once 'phing/tasks/system/condition/IsFileSelected.php';
        $num = array_push($this->conditions, new IsFileSelected());

        return $this->conditions[$num - 1];
    }

    public function createMatches()
    {
        include_once 'phing/tasks/system/condition/Matches.php';
        $num = array_push($this->conditions, new Matches());

        return $this->conditions[$num - 1];
    }

    /**
     * @param  string         $elementName
     * @param  Project        $project
     * @throws BuildException
     * @return Condition
     */
    public function customChildCreator($elementName, Project $project)
    {
        $condition = $project->createCondition($elementName);
        $num = array_push($this->conditions, $condition);

        return $this->conditions[$num - 1];
    }

}

/**
 * "Inner" class for handling enumerations.
 * Uses build-in PHP5 iterator support.
 *
 * @package   phing.tasks.system.condition
 */
class ConditionEnumeration implements Iterator
{

    /** Current element number */
    private $num = 0;

    /** "Outer" ConditionBase class. */
    private $outer;

    /**
     * @param ConditionBase $outer
     */
    public function __construct(ConditionBase $outer)
    {
        $this->outer = $outer;
    }

    /**
     * @return bool
     */
    public function valid()
    {
        return $this->outer->countConditions() > $this->num;
    }

    public function current()
    {
        $o = $this->outer->conditions[$this->num];
        if ($o instanceof ProjectComponent) {
            $o->setProject($this->outer->getProject());
        }

        return $o;
    }

    public function next()
    {
        $this->num++;
    }

    /**
     * @return int
     */
    public function key()
    {
        return $this->num;
    }

    public function rewind()
    {
        $this->num = 0;
    }
}
<?php

/*
 *  $Id: d3373cdf3bbaa92ac40c577198e5a01c6c114796 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/condition/Condition.php';

/**
 * Is one string part of another string?
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Stefan Bodewig <stefan.bodewig@epost.de> (Ant)
 * @version $Id: d3373cdf3bbaa92ac40c577198e5a01c6c114796 $
 * @package phing.tasks.system.condition
 */
class ContainsCondition implements Condition
{

    private $string;
    private $subString;
    private $caseSensitive = true;

    /**
     * The string to search in.
     * @param string $a1
     */
    public function setString($a1)
    {
        $this->string = $a1;
    }

    /**
     * The string to search for.
     * @param string $a2
     */
    public function setSubstring($a2)
    {
        $this->subString = $a2;
    }

    /**
     * Whether to search ignoring case or not.
     * @param $b
     */
    public function setCaseSensitive($b)
    {
        $this->caseSensitive = (boolean) $b;
    }

    /**
     * Check whether string contains substring.
     * @throws BuildException
     */
    public function evaluate()
    {
        if ($this->string === null || $this->subString === null) {
            throw new BuildException("both string and substring are required "
                . "in contains");
        }

        return $this->caseSensitive
            ? strpos($this->string, $this->subString) !== false
            : strpos(strtolower($this->string), strtolower($this->subString)) !== false;
    }
}
<?php
/*
 *  $Id: 624f5e5ba9c48c5b08d31ead22c959c118ca8efa $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/condition/Condition.php';

/**
 * A simple string comparator.  Compares two strings for eqiality in a
 * binary safe manner. Implements the condition interface specification.
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: 624f5e5ba9c48c5b08d31ead22c959c118ca8efa $
 * @package   phing.tasks.system.condition
 */
class EqualsCondition implements Condition
{

    private $arg1;
    private $arg2;
    private $trim = false;
    private $caseSensitive = true;

    /**
     * @param $a1
     */
    public function setArg1($a1)
    {
        $this->arg1 = $a1;
    }

    /**
     * @param $a2
     */
    public function setArg2($a2)
    {
        $this->arg2 = $a2;
    }

    /**
     * Should we want to trim the arguments before comparing them?
     * @param boolean $b
     */
    public function setTrim($b)
    {
        $this->trim = (boolean) $b;
    }

    /**
     * Should the comparison be case sensitive?
     * @param boolean $b
     */
    public function setCaseSensitive($b)
    {
        $this->caseSensitive = (boolean) $b;
    }

    /**
     * @return bool
     * @throws BuildException
     */
    public function evaluate()
    {
        if ($this->arg1 === null || $this->arg2 === null) {
            throw new BuildException("Both arg1 and arg2 are required in equals.");
        }

        if ($this->trim) {
            $this->arg1 = trim($this->arg1);
            $this->arg2 = trim($this->arg2);
        }

        //print("[comparison] Comparing '".$this->arg1."' and '".$this->arg2."'\n");
        return $this->caseSensitive ? $this->arg1 === $this->arg2 : strtolower($this->arg1) === strtolower($this->arg2);
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/tasks/system/condition/Condition.php';

/**
 * Compares two files for equality based on size and
 * content. Timestamps are not at all looked at.
 *
 * @author  Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.tasks.system.condition
 */
class FilesMatch implements Condition
{
    /**
     * files to compare
     */
    private $file1, $file2;

    /**
     * Sets the File1 attribute
     *
     * @param PhingFile $file1 The new File1 value
     */
    public function setFile1(PhingFile $file1)
    {
        $this->file1 = $file1;
    }

    /**
     * Sets the File2 attribute
     *
     * @param PhingFile $file2 The new File2 value
     */
    public function setFile2(PhingFile $file2)
    {
        $this->file2 = $file2;
    }

    /**
     * comparison method of the interface
     *
     * @return bool if the files are equal
     * @throws BuildException if it all went pear-shaped
     */
    public function evaluate()
    {
        if ($this->file1 == null || $this->file2 == null) {
            throw new BuildException("both file1 and file2 are required in filesmatch");
        }

        $fu = new FileUtils;

        return $fu->contentEquals($this->file1, $this->file2);
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/condition/Condition.php';

/**
 * Condition returns true if selected partition has the requested space, false otherwise.
 *
 * @author Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.tasks.system.condition
 */
class HasFreeSpaceCondition implements Condition
{
    /** @var string $partition */
    private $partition;

    /** @var string $needed */
    private $needed;

    /**
     * {@inheritdoc}
     *
     * @throws BuildException
     *
     * @return boolean
     */
    public function evaluate()
    {
        $this->validate();

        $free = disk_free_space($this->partition);
        return $free >= $this->parseHumanSizes($this->needed);
    }

    /**
     * @return void
     *
     * @throws BuildException
     */
    private function validate()
    {
        if (null == $this->partition) {
            throw new BuildException("Please set the partition attribute.");
        }
        if (null == $this->needed) {
            throw new BuildException("Please set the needed attribute.");
        }
    }

    /**
     * Set the partition/device to check.
     *
     * @param $partition
     *
     * @return void
     */
    public function setPartition($partition)
    {
        $this->partition = $partition;
    }

    /**
     * Set the amount of free space required.
     *
     * @return void
     */
    public function setNeeded($needed)
    {
        $this->needed = $needed;
    }

    /**
     * @param string $humanSize
     *
     * @return float
     */
    private function parseHumanSizes($humanSize)
    {
        if (ctype_alpha($char = $humanSize[strlen($humanSize - 1)])) {
            $value = (float) substr($humanSize, 0, strlen($humanSize - 1));
            switch ($char) {
                case 'K':
                    return $value * 1024;
                case 'M':
                    return $value * 1024 * 1024;
                case 'G':
                    return $value * 1024 * 1024 * 1024;
                case 'T':
                    return $value * 1024 * 1024 * 1024 * 1024;
                case 'P':
                    return $value * 1024 * 1024 * 1024 * 1024 * 1024;
                default:
                    return $value;
            }
        } else {
            return (float) $humanSize;
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/ProjectComponent.php';
require_once 'phing/tasks/system/condition/Condition.php';

/**
 * Condition to wait for a HTTP request to succeed.
 *
 * Attributes are:
 * - url - the URL of the request.
 * - errorsBeginAt - number at which errors begin at; default=400.
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing.tasks.system.condition
 */
class HttpCondition extends ProjectComponent implements Condition
{
    private $errorsBeginAt;
    private $url;
    private $quiet = false;

    public function __construct()
    {
        $this->errorsBeginAt = 400;
    }

    /**
     * Set the url attribute.
     *
     * @param string $url the url of the request
     *
     * @return void
     */
    public function setUrl($url)
    {
        $this->url = $url;
    }

    /**
     * Set the errorsBeginAt attribute.
     *
     * @param string $errorsBeginAt number at which errors begin at, default is 400
     *
     * @return void
     */
    public function setErrorsBeginAt($errorsBeginAt)
    {
        $this->errorsBeginAt = $errorsBeginAt;
    }

    /**
     * Set quiet mode, which suppresses warnings if curl_exec() fails.
     * @param $bool
     */
    public function setQuiet($bool)
    {
        $this->quiet = $bool;
    }

    /**
     * {@inheritdoc}
     *
     * @return true if the HTTP request succeeds
     *
     * @throws BuildException if an error occurs
     */
    public function evaluate()
    {
        if ($this->url === null) {
            throw new BuildException("No url specified in http condition");
        }

        if (!filter_var($this->url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED)) {
            $this->log("Possible malformed URL: " . $this->url, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
        }

        $this->log("Checking for " . $this->url, Project::MSG_VERBOSE);

        $handle = curl_init($this->url);
        curl_setopt($handle, CURLOPT_NOBODY, true);

        if (!curl_exec($handle)) {
            $this->log("No response received from URL: " . $this->url, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_ERR);

            return false;
        }

        $httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
        curl_close($handle);

        $this->log("Result code for " . $this->url . " was " . $httpCode, Project::MSG_VERBOSE);

        $result = false;
        if ($httpCode > 0 && $httpCode < $this->errorsBeginAt) {
            $result = true;
        }

        return $result;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/tasks/system/condition/Condition.php';
include_once 'phing/tasks/system/ExecTask.php';

/**
 * Condition to test a return-code for failure.
 *
 * @author  Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.tasks.system
 */
class IsFailure implements Condition
{
    /** @var int $code */
    private $code;

    /**
     * Set the return code to check.
     * @param int $c the return code.
     */
    public function setCode($c)
    {
        $this->code = (int) $c;
    }

    /**
     * Get the return code that will be checked by this IsFailure condition.
     * @return int return code as int.
     */
    public function getCode()
    {
        return $this->code;
    }

    /**
     * Checks whether exitValue signals a failure on the current system.
     *
     * @param int $code
     *
     * @return bool
     */
    protected function isFailureCode($code)
    {
        return $code !== 0;
    }

    /**
     * Fulfill the condition interface.
     * @return boolean the result of evaluating the specified return code.
     */
    public function evaluate()
    {
        return $this->isFailureCode($this->code);
    }
}
<?php
/*
 *  $Id: 9701c89d600f9a53d49b95974dc3bbaa9d84e30a $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/ProjectComponent.php';
require_once 'phing/tasks/system/condition/Condition.php';

/**
 * Condition that tests whether a given string evals to false.
 *
 * @author Hans Lellelid (Phing)
 * @author Steve Loughran (Ant)
 * @version $Id: 9701c89d600f9a53d49b95974dc3bbaa9d84e30a $
 * @package phing.tasks.system.condition
 */
class IsFalseCondition extends ProjectComponent implements Condition
{

    /**
     * what we eval
     */
    private $value;

    /**
     * Set the value to be tested.
     * @param boolean $value
     */
    public function setValue($value)
    {
        $this->value = $value;
    }

    /**
     * return the inverted value;
     * @throws BuildException if someone forgot to spec a value
     */
    public function evaluate()
    {
        if ($this->value === null) {
            throw new BuildException("Nothing to test for falsehood");
        }

        return !$this->value;
    }

}
<?php
/**
 *  $Id: 40daf11b9ba84479da531d295d843421784f512c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */
require_once 'phing/ProjectComponent.php';
require_once 'phing/tasks/system/condition/Condition.php';
require_once 'phing/types/selectors/AbstractSelectorContainer.php';

/**
 * This is a condition that checks to see if a file passes an embedded selector.
 */
class IsFileSelected extends AbstractSelectorContainer implements Condition
{
    /** @var PhingFile $file */
    private $file;
    private $baseDir;

    /**
     * The file to check.
     * @param file the file to check if if passes the embedded selector.
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * The base directory to use.
     * @param baseDir the base directory to use, if null use the project's
     *                basedir.
     */
    public function setBaseDir(PhingFile $baseDir)
    {
        $this->baseDir = $baseDir;
    }

    /**
     * validate the parameters.
     */
    public function validate()
    {
        if ($this->selectorCount() != 1) {
            throw new BuildException("Only one selector allowed");
        }
        parent::validate();
    }

    /**
     * Evaluate the selector with the file.
     * @return true if the file is selected by the embedded selector.
     */
    public function evaluate()
    {
        if ($this->file === null) {
            throw new BuildException('file attribute not set');
        }
        $this->validate();
        $myBaseDir = $this->baseDir;
        if ($myBaseDir === null) {
            $myBaseDir = $this->getProject()->getBaseDir();
        }

        /** @var FileSelector $f */
        $file = $this->getSelectors($this->getProject());
        $f = $file[0];
        return $f->isSelected($myBaseDir, $this->file->getName(), $this->file);
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/condition/ConditionBase.php';
require_once 'phing/tasks/system/condition/Condition.php';

/**
 * Checks the value of a specified property.
 *
 * Returns true if the property evaluates to true.
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing.tasks.system.condition
 */
class IsPropertyFalseCondition extends ConditionBase implements Condition
{
    /** @var string|null $property */
    private $property = null;

    /**
     * @param string $property
     *
     * @return void
     */
    public function setProperty($property)
    {
        $this->property = $property;
    }

    /**
     * @return bool
     *
     * @throws BuildException
     */
    public function evaluate()
    {
        if ($this->property === null) {
            throw new BuildException("Property name must be set.");
        }

        return !(bool) $this->getProject()->getProperty($this->property);
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/condition/ConditionBase.php';
require_once 'phing/tasks/system/condition/Condition.php';

/**
 * Checks the value of a specified property.
 *
 * Returns true if the property evaluates to true.
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing.tasks.system.condition
 */
class IsPropertyTrueCondition extends ConditionBase implements Condition
{
    /** @var string|null $property */
    private $property = null;

    /**
     * @param $property
     *
     * @return void
     */
    public function setProperty($property)
    {
        $this->property = $property;
    }

    /**
     * @return bool
     *
     * @throws BuildException
     */
    public function evaluate()
    {
        if ($this->property === null) {
            throw new BuildException("Property name must be set.");
        }

        return (bool) $this->getProject()->getProperty($this->property);
    }
}
<?php
/*
 *  $Id: e2ad6d80f1e516b7d6bf6a0e384705450b0a2862 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/ProjectComponent.php';
require_once 'phing/tasks/system/condition/Condition.php';

/**
 * Condition that tests whether a given property has been set.
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Stefan Bodewig <stefan.bodewig@epost.de> (Ant)
 * @version $Id: e2ad6d80f1e516b7d6bf6a0e384705450b0a2862 $
 * @package phing.tasks.system.condition
 */
class IsSetCondition extends ProjectComponent implements Condition
{

    private $property;

    /**
     * @param $p
     */
    public function setProperty($p)
    {
        $this->property = $p;
    }

    /**
     * Check whether property is set.
     * @throws BuildException
     */
    public function evaluate()
    {
        if ($this->property === null) {
            throw new BuildException("No property specified for isset "
                . "condition");
        }

        return $this->project->getProperty($this->property) !== null;
    }

}
<?php
/*
 *  $Id: 1e81905398152ad35f75d84e33e9e7c30c1547b3 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/ProjectComponent.php';
require_once 'phing/tasks/system/condition/Condition.php';

/**
 * Condition that tests whether a given string evals to true.
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Steve Loughran (Ant)
 * @package phing.tasks.system.condition
 */
class IsTrueCondition extends ProjectComponent implements Condition
{

    /**
     * what we eval
     */
    private $value;

    /**
     * Set the value to be tested.
     * @param boolean $value
     */
    public function setValue($value)
    {
        $this->value = (bool) $value;
    }

    /**
     * return the inverted value;
     * @throws BuildException if someone forgot to spec a value
     */
    public function evaluate()
    {
        if ($this->value === null) {
            throw new BuildException("Nothing to test for falsehood");
        }

        return $this->value;
    }

}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/condition/Condition.php';
require_once 'phing/ProjectComponent.php';
require_once 'phing/types/RegularExpression.php';
require_once 'phing/BuildException.php';


/**
 * Simple regular expression condition.
 *
 * @author Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.tasks.system.condition
 */
class Matches extends ProjectComponent implements Condition
{
    /** @var string $string */
    private $string;
    
    /** @var RegularExpression $regularExpression */
    private $regularExpression;
    
    /** @var bool $multiLine */
    private $multiLine = false;
    
    /** @var bool $caseSensitive */
    private $caseSensitive = true;
    
    /** @var string $modifiers */
    private $modifiers;

    /**
     * @param boolean $caseSensitive
     */
    public function setCaseSensitive($caseSensitive)
    {
        $this->caseSensitive = $caseSensitive;
    }

    /**
     * Whether to match should be multiline.
     * @param boolean $multiLine
     */
    public function setMultiLine($multiLine)
    {
        $this->multiLine = $multiLine;
    }

    /**
     * @param string $pattern
     * @throws \BuildException
     */
    public function setPattern($pattern)
    {
        if ($this->regularExpression !== null) {
             throw new BuildException('Only one regular expression is allowed.');
         }
         $this->regularExpression = new RegularExpression();
         $this->regularExpression->setPattern($pattern);
    }

    /**
     * The string to match
     * @param string $string
     */
    public function setString($string)
    {
        $this->string = $string;
    }

    /**
     * @param string $modifiers
     */
    public function setModifiers($modifiers)
    {
        $this->modifiers = $modifiers;
    }

    public function evaluate()
    {
        if ($this->string === null) {
            throw new BuildException('Parameter string is required in matches.');
        }
        if ($this->regularExpression === null) {
            throw new BuildException('Missing pattern in matches.');
        }
        $this->regularExpression->setMultiline($this->multiLine);
        $this->regularExpression->setIgnoreCase(!$this->caseSensitive);
        $this->regularExpression->setModifiers($this->modifiers);
        $regexp = $this->regularExpression->getRegexp($this->getProject());
        
        return $regexp->matches($this->string);
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/condition/Condition.php';
require_once 'phing/tasks/system/condition/ConditionBase.php';

/**
 * Nested conditions.
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing.tasks.system.condition
 */
class NestedCondition extends ConditionBase implements Condition
{
    public function evaluate()
    {
        if ($this->countConditions() != 1) {
            throw new BuildException(
                "A single nested condition is required.");
        }
        $cond = $this->getConditions();

        return $cond[0]->evaluate();
    }
}
<?php
/*
 *  $Id: 9bcbe1b79aee7edb15117fbde13481e5e7ec098a $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/condition/ConditionBase.php';

/**
 * <not> condition.
 *
 * Evaluates to true if the single condition nested into it is false
 * and vice versa.
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: 9bcbe1b79aee7edb15117fbde13481e5e7ec098a $
 * @package   phing.tasks.system.condition
 */
class NotCondition extends ConditionBase implements Condition
{

    /**
     * @return bool
     * @throws BuildException
     */
    public function evaluate()
    {
        if ($this->countConditions() > 1) {
            throw new BuildException("You must not nest more than one condition into <not>");
        }
        if ($this->countConditions() < 1) {
            throw new BuildException("You must nest a condition into <not>");
        }
        $conds = $this->getIterator();

        return !$conds->current()->evaluate();
    }
}
<?php
/*
 *  $Id: 7d336fe23714db3d5de330e5cf549e49131308b0 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/condition/ConditionBase.php';

/**
 * <or> condition container.
 *
 * Iterates over all conditions and returns true as soon as one
 * evaluates to true.
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright  2001,2002 THYRELL. All rights reserved
 * @version   $Id: 7d336fe23714db3d5de330e5cf549e49131308b0 $
 * @package   phing.tasks.system.condition
 */
class OrCondition extends ConditionBase implements Condition
{

    /**
     * @return bool
     */
    public function evaluate()
    {
        foreach ($this as $c) { // ConditionBase implements IteratorAggregator
            if ($c->evaluate()) {
                return true;
            }
        }

        return false;
    }
}
<?php
/*
 *  $Id: b10985cbc87aaad2fa6658942033535e7c1d7666 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/condition/ConditionBase.php';

/**
 * Condition that tests the OS type.
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: b10985cbc87aaad2fa6658942033535e7c1d7666 $
 * @package   phing.tasks.system.condition
 */
class OsCondition implements Condition
{

    private $family;

    /**
     * @param $f
     */
    public function setFamily($f)
    {
        $this->family = strtolower($f);
    }

    /**
     * @return bool
     * @throws BuildException
     */
    public function evaluate()
    {
        $osName = strtolower(Phing::getProperty("os.name"));

        if ($this->family !== null) {
            if ($this->family === "windows") {
                return StringHelper::startsWith("win", $osName);
            } elseif ($this->family === "mac") {
                return (strpos($osName, "mac") !== false || strpos($osName, "darwin") !== false);
            } elseif ($this->family === ("unix")) {
                return (
                    StringHelper::endsWith("ix", $osName) ||
                    StringHelper::endsWith("ux", $osName) ||
                    StringHelper::endsWith("bsd", $osName) ||
                    StringHelper::startsWith("sunos", $osName) ||
                    StringHelper::startsWith("darwin", $osName)
                );
            }
            throw new BuildException("Don't know how to detect os family '" . $this->family . "'");
        }

        return false;
    }

}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/BuildException.php';
include_once 'phing/Task.php';
include_once 'phing/tasks/system/condition/Condition.php';

/**
 * An phing version condition/task.
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing.tasks.system
 */
class PhingVersion extends Task implements Condition
{
    private $atLeast = null;
    private $exactly = null;
    private $propertyname = null;

    /**
     * Run as a task.
     * @throws BuildException if an error occurs.
     */
    public function main()
    {
        if ($this->propertyname == null) {
            throw new BuildException("'property' must be set.");
        }
        if ($this->atLeast != null || $this->exactly != null) {
            // If condition values are set, evaluate the condition
            if ($this->evaluate()) {
                $this->getProject()->setNewProperty($this->propertyname, $this->getVersion());
            }
        } else {
            // Raw task
            $this->getProject()->setNewProperty($this->propertyname, $this->getVersion());
        }
    }

    /**
     * Evaluate the condition.
     * @return true if the condition is true.
     * @throws BuildException if an error occurs.
     */
    public function evaluate()
    {
        $this->validate();
        $actual = $this->getVersion();
        if (null != $this->atLeast) {
            return version_compare($actual, $this->atLeast, '>=');
        }

        if (null != $this->exactly) {
            return version_compare($actual, $this->exactly, '=');
        }

        return false;
    }

    private function validate()
    {
        if ($this->atLeast != null && $this->exactly != null) {
            throw new BuildException("Only one of atleast or exactly may be set.");
        }
        if (null == $this->atLeast && null == $this->exactly) {
            throw new BuildException("One of atleast or exactly must be set.");
        }
    }

    private function getVersion()
    {
        $p = new Project();

        return $p->getPhingVersion();
    }

    /**
     * Get the atleast attribute.
     * @return string the atleast attribute.
     */
    public function getAtLeast()
    {
        return $this->atLeast;
    }

    /**
     * Set the atleast attribute.
     * This is of the form major.minor.point.
     * For example 1.7.0.
     * @param string $atLeast the version to check against.
     */
    public function setAtLeast($atLeast)
    {
        $this->atLeast = $atLeast;
    }

    /**
     * Get the exactly attribute.
     * @return string the exactly attribute.
     */
    public function getExactly()
    {
        return $this->exactly;
    }

    /**
     * Set the exactly attribute.
     * This is of the form major.minor.point.
     * For example 1.7.0.
     * @param string $exactly the version to check against.
     */
    public function setExactly($exactly) {
        $this->exactly = $exactly;
    }

    /**
     * Get the name of the property to hold the phing version.
     * @return string the name of the property.
     */
    public function getProperty()
    {
        return $this->propertyname;
    }

    /**
     * Set the name of the property to hold the phing version.
     * @param string $propertyname the name of the property.
     */
    public function setProperty($propertyname)
    {
        $this->propertyname = $propertyname;
    }
}
<?php
/*
 *  $Id: 7321246363e6d75f8a2d47d9dfcd82aaf0038708 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/ProjectComponent.php';
require_once 'phing/tasks/system/condition/Condition.php';

/**
 * Condition that tests whether a given reference exists.
 *
 * @author Matthias Pigulla <mp@webfactory.de> (Phing)
 * @version $Id: 7321246363e6d75f8a2d47d9dfcd82aaf0038708 $
 * @package phing.tasks.system.condition
 */
class ReferenceExistsCondition extends ProjectComponent implements Condition
{

    private $refid;

    /**
     * @param $id
     */
    public function setRef($id)
    {
        $this->refid = (string) $id;
    }

    /**
     * Check whether the reference exists.
     * @throws BuildException
     */
    public function evaluate()
    {
        if ($this->refid === null) {
            throw new BuildException("No ref attribute specified for reference-exists "
                . "condition");
        }
        $refs = $this->project->getReferences();

        return !($refs[$this->refid] instanceof UnknownElement) && isset($refs[$this->refid]);
    }

}
<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/condition/Condition.php';

/**
 * <socket> condition container.
 *
 * Tests for a (tcp) listener on a specified host and port
 *
 * @author  Michiel Rook <mrook@php.net>
 * @package phing.tasks.system.condition
 */
class SocketCondition implements Condition
{

    /**
     * @var string
     */
    private $server;

    /**
     * @var int
     */
    private $port;

    /**
     * @param string $server
     */
    public function setServer($server)
    {
        $this->server = $server;
    }

    /**
     * @param int $port
     */
    public function setPort($port)
    {
        $this->port = $port;
    }

    /**
     * @return boolean
     * @throws BuildException
     */
    public function evaluate()
    {
        if (empty($this->server)) {
            throw new BuildException("No server specified");
        }

        if (empty($this->port)) {
            throw new BuildException("No port specified");
        }

        $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

        if ($socket === false) {
            throw new BuildException("Unable to create socket: " . socket_last_error($socket));
        }

        return @socket_connect($socket, $this->server, $this->port);
    }
}
<?php
/*
 *  $Id: 54de7ff3fbe82f553e57cfeb22ab5ccea8c447ac $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once "phing/tasks/system/condition/Condition.php";

/**
 * Condition that compare versions
 *
 * @author    Tomáš Fejfar (tomas.fejfar@gmail.com)
 * @package   phing.tasks.system.condition
 */
class VersionCompareCondition implements Condition
{

    /**
     * Actual version
     *
     * @var string
     */
    private $version;

    /**
     * Version to be compared to
     *
     * @var string
     */
    private $desiredVersion;

    /**
     * Operator to use (default "greater or equal")
     *
     * @var string operator for possible values @see http://php.net/version%20compare
     */
    private $operator = '>=';

    private $debug = false;

    /**
     * @param $version
     */
    public function setVersion($version)
    {
        $this->version = $version;
    }

    /**
     * @param $desiredVersion
     */
    public function setDesiredVersion($desiredVersion)
    {
        $this->desiredVersion = $desiredVersion;
    }

    /**
     * @param $operator
     * @throws BuildException
     */
    public function setOperator($operator)
    {
        $allowed = array('<', 'lt', '<=', 'le', '>', 'gt', '>=', 'ge', '==', '=', 'eq', '!=', '<>', 'ne');
        if (!in_array($operator, $allowed)) { // allowed operators for php's version_comapare()
            require_once 'phing/BuildException.php';
            throw new BuildException(sprintf(
                'Operator "%s" is not supported. Supported operators: %s',
                $operator,
                implode(', ', $allowed)
            ));
        }
        $this->operator = $operator;
    }

    /**
     * @param $debug
     */
    public function setDebug($debug)
    {
        $this->debug = (bool) $debug;
    }

    /**
     * @return mixed
     * @throws BuildException
     */
    public function evaluate()
    {
        if ($this->version === null || $this->desiredVersion === null) {
            require_once 'phing/BuildException.php';
            throw new BuildException("Missing one version parameter for version compare");
        }
        $isValid = version_compare($this->version, $this->desiredVersion, $this->operator);
        if ($this->debug) {
            echo sprintf(
                'Assertion that %s %s %s failed' . PHP_EOL,
                $this->version,
                $this->operator,
                $this->desiredVersion
             );
        }
        return $isValid;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/condition/ConditionBase.php';
require_once 'phing/tasks/system/condition/Condition.php';

/**
 * The Xor condition type to exclusive or operations. This does not shortcut stuff.
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing.tasks.system.condition
 */
class XorCondition extends ConditionBase implements Condition
{
    /**
     * {@inheritdoc}
     *
     * @return bool
     */
    public function evaluate()
    {
        $conditions = $this->getConditions();
        $state = false;
        foreach ($conditions as $condition) {
            $state ^= $condition->evaluate();
        }

        return $state;
    }
}
<?php
/*
 *  $Id: 6c015291a1baa566c81811171f8dc3765d23ec83 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

require_once 'phing/tasks/system/condition/ConditionBase.php';

/**
 * <condition> task as a generalization of <available>
 *
 * <p>This task supports boolean logic as well as pluggable conditions
 * to decide, whether a property should be set.</p>
 *
 * <p>This task does not extend Task to take advantage of
 * ConditionBase.</p>
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: 6c015291a1baa566c81811171f8dc3765d23ec83 $
 * @package   phing.tasks.system
 */
class ConditionTask extends ConditionBase
{
    /** @var string $property */
    private $property;

    /** @var string $value */
    private $value = "true";

    /** @var string $alternative */
    private $alternative;
    
    /**
     * The name of the property to set. Required.
     * @param string $p
     * @return void
     */
    public function setProperty($p)
    {
        $this->property = $p;
    }

    /**
     * The value for the property to set. Defaults to "true".
     * @param string $v
     * @return void
     */
    public function setValue($v)
    {
        $this->value = $v;
    }

    /**
     * The value for the property to set, if condition evaluates to false.
     * If this attribute is not specified, the property will not be set.
     *
     * @param string $v 
     */
    public function setElse($v)
    {
        $this->alternative = $v;
    }
    
    /**
     * See whether our nested condition holds and set the property.
     * @throws BuildException
     * @return void
     */
    public function main()
    {
        if ($this->countConditions() > 1) {
            throw new BuildException(
                "You must not nest more than one condition into <condition>"
            );
        }
        if ($this->countConditions() < 1) {
            throw new BuildException(
                "You must nest a condition into <condition>"
            );
        }
        if ($this->property === null) {
            throw new BuildException('The property attribute is required.');
        }
        $cs = $this->getIterator();
        if ($cs->current()->evaluate()) {
            $this->log("Condition true; setting " . $this->property . " to " . $this->value, Project::MSG_DEBUG);
            $this->project->setNewProperty($this->property, $this->value);
        } elseif ($this->alternative !== null) {
            $this->log("Condition false; setting " . $this->property . " to " . $this->alternative, Project::MSG_DEBUG);
            $this->project->setNewProperty($this->property, $this->alternative);
        } else {
            $this->log('Condition false; not setting ' . $this->property, Project::MSG_DEBUG);
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/system/io/PhingFile.php';
include_once 'phing/util/FileUtils.php';
include_once 'phing/util/SourceFileScanner.php';
include_once 'phing/mappers/IdentityMapper.php';
include_once 'phing/mappers/FlattenMapper.php';

/**
 * A phing copy task.  Copies a file or directory to a new file
 * or directory.  Files are only copied if the source file is newer
 * than the destination file, or when the destination file does not
 * exist. It is possible to explicitly overwrite existing files.
 *
 * @author   Andreas Aderhold, andi@binarycloud.com
 *
 * @package  phing.tasks.system
 */
class CopyTask extends Task
{
    protected $file = null; // the source file (from xml attribute)
    protected $destFile = null; // the destiantion file (from xml attribute)
    protected $destDir = null; // the destination dir (from xml attribute)
    protected $overwrite = false; // overwrite destination (from xml attribute)
    protected $preserveLMT = false; // sync timestamps (from xml attribute)
    protected $preservePermissions = true; // sync permissions (from xml attribute)
    protected $includeEmpty = true; // include empty dirs? (from XML)
    protected $flatten = false; // apply the FlattenMapper right way (from XML)
    protected $mapperElement = null;

    protected $fileCopyMap = array(); // asoc array containing mapped file names
    protected $dirCopyMap = array(); // asoc array containing mapped file names
    protected $completeDirMap = array(); // asoc array containing complete dir names
    protected $fileUtils = null; // a instance of fileutils
    protected $filesets = array(); // all fileset objects assigned to this task
    protected $filelists = array(); // all filelist objects assigned to this task
    protected $filterChains = array(); // all filterchains objects assigned to this task

    protected $verbosity = Project::MSG_VERBOSE;

    /** @var int $mode */
    protected $mode = 0; // mode to create directories with

    /** @var bool $haltonerror */
    protected $haltonerror = true; // stop build on errors

    protected $enableMultipleMappings = false;

    /**
     * Sets up this object internal stuff.
     * i.e. the Fileutils instance and default mode.
     */
    public function __construct()
    {
        $this->fileUtils = new FileUtils();
        $this->mode = 0777 - umask();
    }

    /**
     * Set the overwrite flag. IntrospectionHelper takes care of
     * booleans in set* methods so we can assume that the right
     * value (boolean primitive) is coming in here.
     *
     * @param  boolean $bool Overwrite the destination file(s) if it/they already exist
     *
     * @return void
     */
    public function setOverwrite($bool)
    {
        $this->overwrite = (boolean) $bool;
    }

    /**
     * Used to force listing of all names of copied files.
     * @param boolean $verbosity
     */
    public function setVerbose($verbosity)
    {
        if ($verbosity) {
            $this->verbosity = Project::MSG_INFO;
        } else {
            $this->verbosity = Project::MSG_VERBOSE;
        }
    }

    /**
     * @see CopyTask::setPreserveLastModified
     * @param $bool
     */
    public function setTstamp($bool)
    {
        $this->setPreserveLastModified($bool);
    }

    /**
     * Set the preserve timestamp flag. IntrospectionHelper takes care of
     * booleans in set* methods so we can assume that the right
     * value (boolean primitive) is coming in here.
     *
     * @param  boolean  Preserve the timestamp on the destination file
     * @return void
     */
    public function setPreserveLastModified($bool)
    {
        $this->preserveLMT = (boolean) $bool;
    }

    /**
     * Set the preserve permissions flag. IntrospectionHelper takes care of
     * booleans in set* methods so we can assume that the right
     * value (boolean primitive) is coming in here.
     *
     * @param  boolean $bool Preserve the timestamp on the destination file
     * @return void
     */
    public function setPreservepermissions($bool)
    {
        $this->preservePermissions = (boolean) $bool;
    }

    /**
     * @param $bool
     */
    public function setPreservemode($bool)
    {
        $this->setPreservepermissions($bool);
    }

    /**
     * Set the include empty dirs flag. IntrospectionHelper takes care of
     * booleans in set* methods so we can assume that the right
     * value (boolean primitive) is coming in here.
     *
     * @param  boolean $bool Flag if empty dirs should be cpoied too
     * @return void
     */
    public function setIncludeEmptyDirs($bool)
    {
        $this->includeEmpty = (boolean) $bool;
    }

    /**
     * Set the file. We have to manually take care of the
     * type that is coming due to limited type support in php
     * in and convert it manually if necessary.
     *
     * @param PhingFile $file The source file. Either a string or an PhingFile object
     *
     * @return void
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * Set the toFile. We have to manually take care of the
     * type that is coming due to limited type support in php
     * in and convert it manually if necessary.
     *
     * @param PhingFile $file The dest file. Either a string or an PhingFile object
     *
     * @return void
     */
    public function setTofile(PhingFile $file)
    {
        $this->destFile = $file;
    }

    /**
     * Sets the mode to create destination directories with (ignored on Windows).
     * Default mode is taken from umask()
     *
     * @param integer $mode Octal mode
     *
     * @return void
     */
    public function setMode($mode)
    {
        $this->mode = (int) base_convert($mode, 8, 10);
    }

    /**
     * Set the toDir. We have to manually take care of the
     * type that is coming due to limited type support in php
     * in and convert it manually if necessary.
     *
     * @param PhingFile $dir The directory, either a string or an PhingFile object
     *
     * @return void
     */
    public function setTodir(PhingFile $dir)
    {
        $this->destDir = $dir;
    }

    public function setEnableMultipleMappings($enableMultipleMappings)
    {
        $this->enableMultipleMappings = (boolean) $enableMultipleMappings;
    }

    public function isEnabledMultipleMappings()
    {
        return $this->enableMultipleMappings;
    }

    /**
     * Set the haltonerror attribute - when true, will
     * make the build fail when errors are detected.
     *
     * @param boolean $haltonerror Flag if the build should be stopped on errors
     *
     * @return void
     */
    public function setHaltonerror($haltonerror)
    {
        $this->haltonerror = (boolean) $haltonerror;
    }

    /**
     * Nested creator, creates a FileSet for this task
     *
     * @param FileSet $fs Set of files to copy
     *
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Nested creator, adds a set of files (nested fileset attribute).
     *
     * @return FileList The created filelist object
     */
    public function createFileList()
    {
        $num = array_push($this->filelists, new FileList());

        return $this->filelists[$num - 1];
    }

    /**
     * Creates a filterchain
     *
     * @return FilterChain The created filterchain object
     */
    public function createFilterChain()
    {
        $num = array_push($this->filterChains, new FilterChain($this->project));

        return $this->filterChains[$num - 1];
    }

    /**
     * Nested creator, creates one Mapper for this task
     *
     * @return Mapper         The created Mapper type object
     * @throws BuildException
     */
    public function createMapper()
    {
        if ($this->mapperElement !== null) {
            throw new BuildException("Cannot define more than one mapper", $this->location);
        }
        $this->mapperElement = new Mapper($this->project);

        return $this->mapperElement;
    }

    /**
     * The main entry point where everything gets in motion.
     *
     * @return true           on success
     * @throws BuildException
     */
    public function main()
    {

        $this->validateAttributes();

        if ($this->file !== null) {
            if ($this->file->exists()) {
                if ($this->destFile === null) {
                    $this->destFile = new PhingFile($this->destDir, (string) $this->file->getName());
                }
                if ($this->overwrite === true || ($this->file->lastModified() > $this->destFile->lastModified())) {
                    $this->fileCopyMap[$this->file->getAbsolutePath()] = $this->destFile->getAbsolutePath();
                } else {
                    $this->log($this->file->getName() . " omitted, is up to date");
                }
            } else {
                // terminate build
                $this->logError("Could not find file " . $this->file->__toString() . " to copy.");
            }
        }

        $project = $this->getProject();

        // process filelists
        foreach ($this->filelists as $fl) {
            $fromDir = $fl->getDir($project);
            $srcFiles = $fl->getFiles($project);
            $srcDirs = array($fl->getDir($project));

            if (!$this->flatten && $this->mapperElement === null) {
                $this->completeDirMap[$fromDir->getAbsolutePath()] = $this->destDir->getAbsolutePath();
            }

            $this->_scan($fromDir, $this->destDir, $srcFiles, $srcDirs);
        }

        // process filesets
        foreach ($this->filesets as $fs) {
            try {
                $ds = $fs->getDirectoryScanner($project);
                $fromDir = $fs->getDir($project);
                $srcFiles = $ds->getIncludedFiles();
                $srcDirs = $ds->getIncludedDirectories();

                if (!$this->flatten && $this->mapperElement === null &&
                    $ds->isEverythingIncluded()
                ) {
                    $this->completeDirMap[$fromDir->getAbsolutePath()] = $this->destDir->getAbsolutePath();
                }

                $this->_scan($fromDir, $this->destDir, $srcFiles, $srcDirs);
            } catch (BuildException $e) {
                if ($this->haltonerror == true) {
                    throw $e;
                }

                $this->logError($e->getMessage());
            }
        }

        // go and copy the stuff
        $this->doWork();

        if ($this->destFile !== null) {
            $this->destDir = null;
        }
    }

    /**
     * Validates attributes coming in from XML
     *
     * @return void
     * @throws BuildException
     */
    protected function validateAttributes()
    {

        if ($this->file === null && count($this->filesets) === 0 && count($this->filelists) === 0) {
            throw new BuildException("CopyTask. Specify at least one source - a file, fileset or filelist.");
        }

        if ($this->destFile !== null && $this->destDir !== null) {
            throw new BuildException("Only one of destfile and destdir may be set.");
        }

        if ($this->destFile === null && $this->destDir === null) {
            throw new BuildException("One of destfile or destdir must be set.");
        }

        if ($this->file !== null && $this->file->exists() && $this->file->isDirectory()) {
            throw new BuildException("Use a fileset to copy directories.");
        }

        if ($this->destFile !== null && count($this->filesets) > 0) {
            throw new BuildException("Cannot concatenate multiple files into a single file.");
        }

        if ($this->destFile !== null) {
            $this->destDir = new PhingFile($this->destFile->getParent());
        }
    }

    /**
     * Compares source files to destination files to see if they
     * should be copied.
     *
     * @param $fromDir
     * @param $toDir
     * @param $files
     * @param $dirs
     *
     * @return void
     */
    private function _scan(&$fromDir, &$toDir, &$files, &$dirs)
    {
        /* mappers should be generic, so we get the mappers here and
        pass them on to builMap. This method is not redundan like it seems */
        $mapper = $this->getMapper();
        
        $this->buildMap($fromDir, $toDir, $files, $mapper, $this->fileCopyMap);
        
        if ($this->includeEmpty) {
            $this->buildMap($fromDir, $toDir, $dirs, $mapper, $this->dirCopyMap);
        }
    }

    private function getMapper()
    {
        $mapper = null;
        if ($this->mapperElement !== null) {
            $mapper = $this->mapperElement->getImplementation();
        } elseif ($this->flatten) {
            $mapper = new FlattenMapper();
        } else {
            $mapper = new IdentityMapper();
        }
        return $mapper;
    }

    /**
     * Builds a map of filenames (from->to) that should be copied
     *
     * @param $fromDir
     * @param $toDir
     * @param $names
     * @param $mapper
     * @param $map
     *
     * @return void
     */
    private function buildMap(&$fromDir, &$toDir, &$names, &$mapper, &$map)
    {
        $toCopy = null;
        if ($this->overwrite) {
            $v = array();
            foreach ($names as $name) {
                $result = $mapper->main($name);
                if ($result !== null) {
                    $v[] = $name;
                }
            }
            $toCopy = $v;
        } else {
            $ds = new SourceFileScanner($this);
            $toCopy = $ds->restrict($names, $fromDir, $toDir, $mapper);
        }

        for ($i = 0, $_i = count($toCopy); $i < $_i; $i++) {
            $src = new PhingFile($fromDir, $toCopy[$i]);
            $mapped = $mapper->main($toCopy[$i]);
            if (!$this->enableMultipleMappings) {
                $dest = new PhingFile($toDir, $mapped[0]);
                $map[$src->getAbsolutePath()] = $dest->getAbsolutePath();
            } else {
                foreach ($mapped as $mappedFile) {
                    if ($mappedFile === null) {
                        continue;
                    }
                    $dest = new PhingFile($toDir, $mappedFile);
                    $mappedFiles[] = $dest->getAbsolutePath();
                }
                $map[$src->getAbsolutePath()] = $mappedFiles;
            }
        }
    }

    /**
     * Actually copies the files
     *
     * @return void
     * @throws BuildException
     */
    protected function doWork()
    {

        // These "slots" allow filters to retrieve information about the currently-being-process files
        $fromSlot = $this->getRegisterSlot("currentFromFile");
        $fromBasenameSlot = $this->getRegisterSlot("currentFromFile.basename");

        $toSlot = $this->getRegisterSlot("currentToFile");
        $toBasenameSlot = $this->getRegisterSlot("currentToFile.basename");

        $mapSize = count($this->fileCopyMap);
        $total = $mapSize;

        // handle empty dirs if appropriate
        if ($this->includeEmpty) {
            $count = 0;
            foreach ($this->dirCopyMap as $srcdir => $destdir) {
                $s = new PhingFile((string) $srcdir);
                $d = new PhingFile((string) $destdir);
                if (!$d->exists()) {

                    // Setting source directory permissions to target
                    // (On permissions preservation, the target directory permissions
                    // will be inherited from the source directory, otherwise the 'mode'
                    // will be used)
                    $dirMode = ($this->preservePermissions ? $s->getMode() : $this->mode);

                    // Directory creation with specific permission mode
                    if (!$d->mkdirs($dirMode)) {
                        $this->logError("Unable to create directory " . $d->__toString());
                    } else {
                        if ($this->preserveLMT) {
                            $d->setLastModified($s->lastModified());
                        }

                        $count++;
                    }
                }
            }
            if ($count > 0) {
                $this->log(
                    "Created " . $count . " empty director" . ($count == 1 ? "y" : "ies") . " in " . $this->destDir->getAbsolutePath(
                    )
                );
            }
        }

        if ($mapSize == 0) {
            return;
        }

        $this->log(
            "Copying " . $mapSize . " file" . (($mapSize) === 1 ? '' : 's') . " to " . $this->destDir->getAbsolutePath(
            )
        );
        // walks the map and actually copies the files
        $count = 0;
        foreach ($this->fileCopyMap as $from => $toFiles) {
            if (is_array($toFiles)) {
                foreach ($toFiles as $to) {
                    $this->copyToSingleDestination($from, $to, $fromSlot, $fromBasenameSlot, $toSlot, $toBasenameSlot, $count, $total);
                }
            } else {
                $this->copyToSingleDestination($from, $toFiles, $fromSlot, $fromBasenameSlot, $toSlot, $toBasenameSlot, $count, $total);
            }
        }
    }

    private function copyToSingleDestination($from, $to, $fromSlot, $fromBasenameSlot, $toSlot, $toBasenameSlot, &$count, &$total)
    {
        if ($from === $to) {
            $this->log("Skipping self-copy of " . $from, $this->verbosity);
            $total--;
            return;
        }
        $this->log("From " . $from . " to " . $to, $this->verbosity);
        try { // try to copy file

            $fromFile = new PhingFile($from);
            $toFile = new PhingFile($to);

            $fromSlot->setValue($fromFile->getPath());
            $fromBasenameSlot->setValue($fromFile->getName());

            $toSlot->setValue($toFile->getPath());
            $toBasenameSlot->setValue($toFile->getName());

            $this->fileUtils->copyFile(
                $fromFile,
                $toFile,
                $this->overwrite,
                $this->preserveLMT,
                $this->filterChains,
                $this->getProject(),
                $this->mode,
                $this->preservePermissions
            );

            $count++;
        } catch (IOException $ioe) {
            $this->logError("Failed to copy " . $from . " to " . $to . ": " . $ioe->getMessage());
        }
    }

    /**
     * @param string $message
     * @param null $location
     *
     * @throws BuildException
     */
    protected function logError($message, $location = null)
    {
        if ($this->haltonerror) {
            throw new BuildException($message, $location);
        } else {
            $this->log($message, Project::MSG_ERR);
        }
    }
}
<?php
/*
 *  $Id: d3d7f451134334520e03ba410d4e3ddd5beb1bfe $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/system/io/BufferedReader.php';
include_once 'phing/system/io/BufferedWriter.php';
include_once 'phing/util/StringHelper.php';

/**
 * Adds an new entry to a CVS password file.
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Jeff Martin <jeff@custommonkey.org> (Ant)
 * @version $Id: d3d7f451134334520e03ba410d4e3ddd5beb1bfe $
 * @package phing.tasks.system
 */
class CvsPassTask extends Task
{

    /** CVS Root */
    private $cvsRoot;
    /** Password file to add password to */
    private $passFile;
    /** Password to add to file */
    private $password;

    /** Array contain char conversion data */
    private static $shifts = array(
        0,
        1,
        2,
        3,
        4,
        5,
        6,
        7,
        8,
        9,
        10,
        11,
        12,
        13,
        14,
        15,
        16,
        17,
        18,
        19,
        20,
        21,
        22,
        23,
        24,
        25,
        26,
        27,
        28,
        29,
        30,
        31,
        114,
        120,
        53,
        79,
        96,
        109,
        72,
        108,
        70,
        64,
        76,
        67,
        116,
        74,
        68,
        87,
        111,
        52,
        75,
        119,
        49,
        34,
        82,
        81,
        95,
        65,
        112,
        86,
        118,
        110,
        122,
        105,
        41,
        57,
        83,
        43,
        46,
        102,
        40,
        89,
        38,
        103,
        45,
        50,
        42,
        123,
        91,
        35,
        125,
        55,
        54,
        66,
        124,
        126,
        59,
        47,
        92,
        71,
        115,
        78,
        88,
        107,
        106,
        56,
        36,
        121,
        117,
        104,
        101,
        100,
        69,
        73,
        99,
        63,
        94,
        93,
        39,
        37,
        61,
        48,
        58,
        113,
        32,
        90,
        44,
        98,
        60,
        51,
        33,
        97,
        62,
        77,
        84,
        80,
        85,
        223,
        225,
        216,
        187,
        166,
        229,
        189,
        222,
        188,
        141,
        249,
        148,
        200,
        184,
        136,
        248,
        190,
        199,
        170,
        181,
        204,
        138,
        232,
        218,
        183,
        255,
        234,
        220,
        247,
        213,
        203,
        226,
        193,
        174,
        172,
        228,
        252,
        217,
        201,
        131,
        230,
        197,
        211,
        145,
        238,
        161,
        179,
        160,
        212,
        207,
        221,
        254,
        173,
        202,
        146,
        224,
        151,
        140,
        196,
        205,
        130,
        135,
        133,
        143,
        246,
        192,
        159,
        244,
        239,
        185,
        168,
        215,
        144,
        139,
        165,
        180,
        157,
        147,
        186,
        214,
        176,
        227,
        231,
        219,
        169,
        175,
        156,
        206,
        198,
        129,
        164,
        150,
        210,
        154,
        177,
        134,
        127,
        182,
        128,
        158,
        208,
        162,
        132,
        167,
        209,
        149,
        241,
        153,
        251,
        237,
        236,
        171,
        195,
        243,
        233,
        253,
        240,
        194,
        250,
        191,
        155,
        142,
        137,
        245,
        235,
        163,
        242,
        178,
        152
    );

    /**
     * Create a CVS task using the default cvspass file location.
     */
    public function __construct()
    {
        $this->passFile = new PhingFile(
            Phing::getProperty(
                "cygwin.user.home",
                Phing::getProperty("user.home")
            )
            . DIRECTORY_SEPARATOR . ".cvspass");
    }

    /**
     * Does the work.
     *
     * @throws BuildException if someting goes wrong with the build
     */
    final public function main()
    {
        if ($this->cvsRoot === null) {
            throw new BuildException("cvsroot is required");
        }
        if ($this->password === null) {
            throw new BuildException("password is required");
        }

        $this->log("cvsRoot: " . $this->cvsRoot, Project::MSG_DEBUG);
        $this->log("password: " . $this->password, Project::MSG_DEBUG);
        $this->log("passFile: " . $this->passFile->__toString(), Project::MSG_DEBUG);

        $reader = null;
        $writer = null;

        try {
            $buf = "";

            if ($this->passFile->exists()) {
                $reader = new BufferedReader(new FileReader($this->passFile));

                $line = null;
                while (($line = $reader->readLine()) !== null) {
                    if (!StringHelper::startsWith($this->cvsRoot, $line)) {
                        $buf .= $line . PHP_EOL;
                    }
                }
            }

            $pwdfile = $buf . $this->cvsRoot . " A" . $this->mangle($this->password);

            $this->log("Writing -> " . $pwdfile, Project::MSG_DEBUG);

            $writer = new BufferedWriter(new FileWriter($this->passFile));
            $writer->write($pwdfile);
            $writer->newLine();

            $writer->close();
            if ($reader) {
                $reader->close();
            }

        } catch (IOException $e) {
            if ($reader) {
                try {
                    $reader->close();
                } catch (Exception $e) {
                }
            }

            if ($writer) {
                try {
                    $writer->close();
                } catch (Exception $e) {
                }
            }

            throw new BuildException($e);
        }
    }

    /**
     * "Encode" the password.
     * @param $password
     * @return string
     */
    final private function mangle($password)
    {
        $buf = "";
        for ($i = 0, $plen = strlen($password); $i < $plen; $i++) {
            $buf .= chr(self::$shifts[ord($password{$i})]);
        }

        return $buf;
    }

    /**
     * The CVS repository to add an entry for.
     * @param string $cvsRoot
     */
    public function setCvsroot($cvsRoot)
    {
        $this->cvsRoot = $cvsRoot;
    }

    /**
     * Password file to add the entry to.
     * @param PhingFile $passFile
     */
    public function setPassfile(PhingFile $passFile)
    {
        $this->passFile = $passFile;
    }

    /**
     * Password to be added to the password file.
     * @param string $password
     */
    public function setPassword($password)
    {
        $this->password = $password;
    }

}
<?php
/*
 *  $Id: c8232b68045eec9d40b1bda18c4b758c6cfbd80f $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/tasks/system/ExecTask.php';
include_once 'phing/types/Commandline.php';

/**
 * Task for performing CVS operations.
 *
 *  NOTE: This implementation has been moved here from Cvs.java with
 *  the addition of some accessors for extensibility.  Another task
 *  can extend this with some customized output processing.
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author costin@dnt.ro (Ant)
 * @author stefano@apache.org (Ant)
 * @author Wolfgang Werner <wwerner@picturesafe.de> (Ant)
 * @author Kevin Ross <kevin.ross@bredex.com> (Ant)
 * @version $Id: c8232b68045eec9d40b1bda18c4b758c6cfbd80f $
 * @package phing.tasks.system
 */
class CvsTask extends Task
{

    /**
     * Default compression level to use, if compression is enabled via
     * setCompression( true ).
     */
    const DEFAULT_COMPRESSION_LEVEL = 3;

    private $cmd;

    /**
     * List of Commandline children
     * @var array Commandline[]
     */
    private $commandlines = array();

    /**
     * the CVSROOT variable.
     */
    private $cvsRoot;

    /**
     * the CVS_RSH variable.
     */
    private $cvsRsh;

    /**
     * the package/module to check out.
     */
    private $cvsModule;

    /**
     * the default command.
     */
    private static $default_command = "checkout";

    /**
     * the CVS command to execute.
     */
    private $command = null;

    /**
     * suppress information messages.
     */
    private $quiet = false;

    /**
     * compression level to use.
     */
    private $compression = 0;

    /**
     * report only, don't change any files.
     */
    private $noexec = false;

    /**
     * CVS port
     */
    private $port = 0;

    /**
     * CVS password file
     * @var File
     */
    private $passFile = null;

    /**
     * the directory where the checked out files should be placed.
     * @var File
     */
    private $dest;

    private $error;

    private $output;

    /**
     * If true it will stop the build if cvs exits with error.
     * Default is false. (Iulian)
     * @var boolean
     */
    private $failOnError = false;

    public function init()
    {
        $this->cmd = new Commandline();
    }

    /**
     * Sets up the environment for toExecute and then runs it.
     * @param  Commandline    $toExecute
     * @throws BuildException
     */
    protected function runCommand(Commandline $toExecute)
    {

        // We are putting variables into the script's environment
        // and not removing them (!)  This should be fine, but is
        // worth remembering and testing.

        if ($this->port > 0) {
            putenv("CVS_CLIENT_PORT=" . $this->port);
        }

        // Need a better cross platform integration with <cvspass>, so
        // use the same filename.

        if ($this->passFile === null) {
            $defaultPassFile = new PhingFile(Phing::getProperty("cygwin.user.home", Phing::getProperty("user.home"))
                . DIRECTORY_SEPARATOR . ".cvspass");
            if ($defaultPassFile->exists()) {
                $this->setPassfile($defaultPassFile);
            }
        }

        if ($this->passFile !== null) {
            if ($this->passFile->isFile() && $this->passFile->canRead()) {
                putenv("CVS_PASSFILE=" . $this->passFile->__toString());
                $this->log("Using cvs passfile: " . $this->passFile->__toString(), Project::MSG_INFO);
            } elseif (!$this->passFile->canRead()) {
                $this->log(
                    "cvs passfile: " . $this->passFile->__toString()
                    . " ignored as it is not readable",
                    Project::MSG_WARN
                );
            } else {
                $this->log(
                    "cvs passfile: " . $this->passFile->__toString()
                    . " ignored as it is not a file",
                    Project::MSG_WARN
                );
            }
        }

        if ($this->cvsRsh !== null) {
            putenv("CVS_RSH=" . $this->cvsRsh);
        }

        // Use the ExecTask to handle execution of the command
        $exe = new ExecTask($this->project);
        $exe->setProject($this->project);

        //exe.setAntRun(project);
        if ($this->dest === null) {
            $this->dest = $this->project->getBaseDir();
        }

        if (!$this->dest->exists()) {
            $this->dest->mkdirs();
        }

        if ($this->output !== null) {
            $exe->setOutput($this->output);
        }

        if ($this->error !== null) {
            $exe->setError($this->error);
        }

        $exe->setDir($this->dest);

        if (is_object($toExecute)) {
            $toExecuteStr = $toExecute->__toString(); // unfortunately no more automagic for initial 5.0.0 release :(
        }

        $exe->setCommand($toExecuteStr);

        try {
            $actualCommandLine = $toExecuteStr; // we converted to string above
            $this->log($actualCommandLine, Project::MSG_INFO);
            $retCode = $exe->main();
            $this->log("retCode=" . $retCode, Project::MSG_DEBUG);
            /*Throw an exception if cvs exited with error. (Iulian)*/
            if ($this->failOnError && $retCode !== 0) {
                throw new BuildException("cvs exited with error code "
                    . $retCode
                    . PHP_EOL
                    . "Command line was ["
                    . $toExecute->describeCommand() . "]", $this->getLocation());
            }
        } catch (IOException $e) {
            if ($this->failOnError) {
                throw new BuildException($e, $this->getLocation());
            } else {
                $this->log("Caught exception: " . $e, Project::MSG_WARN);
            }
        } catch (BuildException $e) {
            if ($this->failOnError) {
                throw $e;
            } else {
                $t = $e->getCause();
                if ($t === null) {
                    $t = $e;
                }
                $this->log("Caught exception: " . $t, Project::MSG_WARN);
            }
        } catch (Exception $e) {
            if ($this->failOnError) {
                throw new BuildException($e, $this->getLocation());
            } else {
                $this->log("Caught exception: " . $e, Project::MSG_WARN);
            }
        }
    }

    /**
     *
     * @throws Exception
     * @return void
     */
    public function main()
    {

        $savedCommand = $this->getCommand();

        if ($this->getCommand() === null && empty($this->commandlines)) {
            // re-implement legacy behaviour:
            $this->setCommand(self::$default_command);
        }

        $c = $this->getCommand();
        $cloned = null;
        if ($c !== null) {
            $cloned = $this->cmd->__copy();
            $cloned->createArgument(true)->setLine($c);
            $this->addConfiguredCommandline($cloned, true);
        }

        try {
            for ($i = 0, $vecsize = count($this->commandlines); $i < $vecsize; $i++) {
                $this->runCommand($this->commandlines[$i]);
            }

            // finally    {
            if ($cloned !== null) {
                $this->removeCommandline($cloned);
            }
            $this->setCommand($savedCommand);

        } catch (Exception $e) {
            // finally {
            if ($cloned !== null) {
                $this->removeCommandline($cloned);
            }
            $this->setCommand($savedCommand);
            throw $e;
        }
    }

    /**
     * The CVSROOT variable.
     *
     * @param string $root
     */
    public function setCvsRoot($root)
    {

        // Check if not real cvsroot => set it to null
        if ($root !== null) {
            if (trim($root) == "") {
                $root = null;
            }
        }

        $this->cvsRoot = $root;
    }

    public function getCvsRoot()
    {
        return $this->cvsRoot;
    }

    /**
     * The CVS_RSH variable.
     *
     * @param rsh
     */
    public function setCvsRsh($rsh)
    {
        // Check if not real cvsrsh => set it to null
        if ($rsh !== null) {
            if (trim($rsh) == "") {
                $rsh = null;
            }
        }

        $this->cvsRsh = $rsh;
    }

    public function getCvsRsh()
    {
        return $this->cvsRsh;
    }

    /**
     * Port used by CVS to communicate with the server.
     *
     * @param int $port
     */
    public function setPort($port)
    {
        $this->port = $port;
    }

    /**
     * @return int
     */
    public function getPort()
    {
        return $this->port;
    }

    /**
     * Password file to read passwords from.
     *
     * @param PhingFile $passFile
     * @internal param $passFile
     */
    public function setPassfile(PhingFile $passFile)
    {
        $this->passFile = $passFile;
    }

    /**
     * @return File
     */
    public function getPassFile()
    {
        return $this->passFile;
    }

    /**
     * The directory where the checked out files should be placed.
     *
     * @param PhingFile $dest
     */
    public function setDest(PhingFile $dest)
    {
        $this->dest = $dest;
    }

    /**
     * @return File
     */
    public function getDest()
    {
        return $this->dest;
    }

    /**
     * The package/module to operate upon.
     *
     * @param $m
     * @internal param string $p
     */
    public function setModule($m)
    {
        $this->cvsModule = $m;
    }

    public function getModule()
    {
        return $this->cvsModule;
    }

    /**
     * The tag of the package/module to operate upon.
     * @param string $p
     */
    public function setTag($p)
    {
        // Check if not real tag => set it to null
        if ($p !== null && trim($p) !== "") {
            $this->appendCommandArgument("-r");
            $this->appendCommandArgument($p);
        }
    }

    /**
     * This needs to be public to allow configuration
     *      of commands externally.
     * @param $arg
     */
    public function appendCommandArgument($arg)
    {
        $this->cmd->createArgument()->setValue($arg);
    }

    /**
     * Use the most recent revision no later than the given date.
     * @param p
     */
    public function setDate($p)
    {
        if ($p !== null && trim($p) !== "") {
            $this->appendCommandArgument("-D");
            $this->appendCommandArgument($p);
        }
    }

    /**
     * The CVS command to execute.
     * @param string $c
     */
    public function setCommand($c)
    {
        $this->command = $c;
    }

    /**
     * @return null
     */
    public function getCommand()
    {
        return $this->command;
    }

    /**
     * If true, suppress informational messages.
     * @param boolean $q
     */
    public function setQuiet($q)
    {
        $this->quiet = $q;
    }

    /**
     * If true, report only and don't change any files.
     *
     * @param boolean $ne
     */
    public function setNoexec($ne)
    {
        $this->noexec = (boolean) $ne;
    }

    /**
     * Stop the build process if the command exits with
     * a return code other than 0.
     * Defaults to false.
     * @param boolean $failOnError
     */
    public function setFailOnError($failOnError)
    {
        $this->failOnError = (boolean) $failOnError;
    }

    /**
     * Configure a commandline element for things like cvsRoot, quiet, etc.
     * @param $c
     * @return string
     */
    protected function configureCommandline($c)
    {
        if ($c === null) {
            return;
        }
        $c->setExecutable("cvs");

        if ($this->cvsModule !== null) {
            $c->createArgument()->setLine($this->cvsModule);
        }
        if ($this->compression > 0 && $this->compression < 10) {
            $c->createArgument(true)->setValue("-z" . $this->compression);
        }
        if ($this->quiet) {
            $c->createArgument(true)->setValue("-q");
        }
        if ($this->noexec) {
            $c->createArgument(true)->setValue("-n");
        }
        if ($this->cvsRoot !== null) {
            $c->createArgument(true)->setLine("-d" . $this->cvsRoot);
        }
    }

    /**
     * @param Commandline $c
     * @return bool
     */
    protected function removeCommandline(Commandline $c)
    {
        $idx = array_search($c, $this->commandlines, true);
        if ($idx === false) {
            return false;
        }
        $this->commandlines = array_splice($this->commandlines, $idx, 1);

        return true;
    }

    /**
     * Configures and adds the given Commandline.
     * @param Commandline $c
     * @param bool|If $insertAtStart
     * @internal param If $insertAtStart true, c is
     */
    public function addConfiguredCommandline(Commandline $c, $insertAtStart = false)
    {
        if ($c === null) {
            return;
        }
        $this->configureCommandline($c);
        if ($insertAtStart) {
            array_unshift($this->commandlines, $c);
        } else {
            array_push($this->commandlines, $c);
        }
    }

    /**
     * If set to a value 1-9 it adds -zN to the cvs command line, else
     * it disables compression.
     * @param int $level
     */
    public function setCompressionLevel($level)
    {
        $this->compression = $level;
    }

    /**
     * If true, this is the same as compressionlevel="3".
     *
     * @param boolean $usecomp If true, turns on compression using default
     *                         level, AbstractCvsTask.DEFAULT_COMPRESSION_LEVEL.
     */
    public function setCompression($usecomp)
    {
        $this->setCompressionLevel(
            $usecomp ?
                self::DEFAULT_COMPRESSION_LEVEL : 0
        );
    }

    /**
     * File to which output should be written.
     * @param PhingFile $f
     * @internal param PhingFile $output
     */
    public function setOutput(PhingFile $f)
    {
        $this->output = $f;
    }

    /**
     * File to which error output should be written.
     * @param PhingFile $f
     * @internal param PhingFile $output
     */
    public function setError(PhingFile $f)
    {
        $this->error = $f;
    }

}
<?php
/*
 *  $Id: 4c715ad7eb460825b33303a50fbca1d25599cbdf $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Deletes a file or directory, or set of files defined by a fileset.
 *
 * @version   $Id: 4c715ad7eb460825b33303a50fbca1d25599cbdf $
 * @package   phing.tasks.system
 */
class DeleteTask extends Task
{

    protected $file;
    protected $dir;
    protected $filesets = array();
    protected $includeEmpty = false;

    protected $quiet = false;
    protected $failonerror = false;
    protected $verbosity = Project::MSG_VERBOSE;

    /** Any filelists of files that should be deleted. */
    private $filelists = array();

    /**
     * Set the name of a single file to be removed.
     * @param PhingFile $file
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * Set the directory from which files are to be deleted.
     * @param PhingFile $dir
     */
    public function setDir(PhingFile $dir)
    {
        $this->dir = $dir;
    }

    /**
     * Used to force listing of all names of deleted files.
     * @param boolean $verbosity
     */
    public function setVerbose($verbosity)
    {
        if ($verbosity) {
            $this->verbosity = Project::MSG_INFO;
        } else {
            $this->verbosity = Project::MSG_VERBOSE;
        }
    }

    /**
     * If the file does not exist, do not display a diagnostic
     * message or modify the exit status to reflect an error.
     * This means that if a file or directory cannot be deleted,
     * then no error is reported. This setting emulates the
     * -f option to the Unix rm command. Default is false
     * meaning things are verbose
     * @param bool $bool
     * @return void
     */
    public function setQuiet($bool)
    {
        $this->quiet = $bool;
        if ($this->quiet) {
            $this->failonerror = false;
        }
    }

    /** this flag means 'note errors to the output, but keep going'
     * @param bool $bool
     * @retujrn void
     */
    public function setFailOnError($bool)
    {
        $this->failonerror = $bool;
    }

    /**
     * Used to delete empty directories.
     * @param bool $includeEmpty
     * @return void
     */
    public function setIncludeEmptyDirs($includeEmpty)
    {
        $this->includeEmpty = (boolean) $includeEmpty;
    }

    /**
     * Nested creator, adds a set of files (nested fileset attribute).
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Nested creator, adds a set of files (nested fileset attribute).
     * @return FileList
     */
    public function createFileList()
    {
        $num = array_push($this->filelists, new FileList());

        return $this->filelists[$num - 1];
    }

    /**
     * Delete the file(s).
     * @throws BuildException
     */
    public function main()
    {
        if ($this->file === null && $this->dir === null && count($this->filesets) === 0 && count(
                $this->filelists
            ) === 0
        ) {
            throw new BuildException("At least one of the file or dir attributes, or a fileset element, or a filelist element must be set.");
        }

        if ($this->quiet && $this->failonerror) {
            throw new BuildException("quiet and failonerror cannot both be set to true", $this->location);
        }

        // delete a single file
        if ($this->file !== null) {
            if ($this->file->exists()) {
                if ($this->file->isDirectory()) {
                    $this->log(
                        "Directory " . $this->file->__toString(
                        ) . " cannot be removed using the file attribute. Use dir instead."
                    );
                } else {
                    $this->log("Deleting: " . $this->file->__toString());
                    try {
                        $this->file->delete();
                    } catch (Exception $e) {
                        $message = "Unable to delete file " . $this->file->__toString() . ": " . $e->getMessage();
                        if ($this->failonerror) {
                            throw new BuildException($message);
                        } else {
                            $this->log($message, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
                        }
                    }
                }
            } else {
                $message = "Could not find file " . $this->file->getAbsolutePath() . " to delete.";

                if ($this->failonerror) {
                    throw new BuildException($message);
                } else {
                    $this->log($message, ($this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN));
                }
            }
        }

        // delete the directory
        if ($this->dir !== null) {
            if ($this->dir->exists() && $this->dir->isDirectory()) {
                if ($this->verbosity === Project::MSG_VERBOSE) {
                    $this->log("Deleting directory " . $this->dir->__toString());
                }
                $this->removeDir($this->dir);
            } else {
                $message = "Directory " . $this->dir->getAbsolutePath() . " does not exist or is not a directory.";

                if ($this->failonerror) {
                    throw new BuildException($message);
                } else {
                    $this->log($message, ($this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN));
                }
            }
        }

        // delete the files in the filelists
        foreach ($this->filelists as $fl) {
            try {
                $files = $fl->getFiles($this->project);
                $this->removeFiles($fl->getDir($this->project), $files, $empty = array());
            } catch (BuildException $be) {
                // directory doesn't exist or is not readable
                if ($this->failonerror) {
                    throw $be;
                } else {
                    $this->log($be->getMessage(), $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
                }
            }
        }

        // delete the files in the filesets
        foreach ($this->filesets as $fs) {
            try {
                $ds = $fs->getDirectoryScanner($this->project);
                $files = $ds->getIncludedFiles();
                $dirs = $ds->getIncludedDirectories();
                $this->removeFiles($fs->getDir($this->project), $files, $dirs);
            } catch (BuildException $be) {
                // directory doesn't exist or is not readable
                if ($this->failonerror) {
                    throw $be;
                } else {
                    $this->log($be->getMessage(), $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
                }
            }
        }
    }

    /**
     * Recursively removes a directory.
     * @param PhingFile $d The directory to remove.
     * @throws BuildException
     */
    private function removeDir($d)
    {
        $list = $d->listDir();
        if ($list === null) {
            $list = array();
        }

        foreach ($list as $s) {
            $f = new PhingFile($d, $s);
            if ($f->isDirectory()) {
                $this->removeDir($f);
            } else {
                $this->log("Deleting " . $f->__toString(), $this->verbosity);
                try {
                    $f->delete();
                } catch (Exception $e) {
                    $message = "Unable to delete file " . $f->__toString() . ": " . $e->getMessage();
                    if ($this->failonerror) {
                        throw new BuildException($message);
                    } else {
                        $this->log($message, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
                    }
                }
            }
        }
        $this->log("Deleting directory " . $d->getAbsolutePath(), $this->verbosity);
        try {
            $d->delete();
        } catch (Exception $e) {
            $message = "Unable to delete directory " . $d->__toString() . ": " . $e->getMessage();
            if ($this->failonerror) {
                throw new BuildException($message);
            } else {
                $this->log($message, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
            }
        }
    }

    /**
     * remove an array of files in a directory, and a list of subdirectories
     * which will only be deleted if 'includeEmpty' is true
     * @param PhingFile $d directory to work from
     * @param array &$files array of files to delete; can be of zero length
     * @param array &$dirs array of directories to delete; can of zero length
     * @throws BuildException
     */
    private function removeFiles(PhingFile $d, &$files, &$dirs)
    {
        if (count($files) > 0) {
            $this->log("Deleting " . count($files) . " files from " . $d->__toString());
            for ($j = 0, $_j = count($files); $j < $_j; $j++) {
                $f = new PhingFile($d, $files[$j]);
                $this->log("Deleting " . $f->getAbsolutePath(), $this->verbosity);
                try {
                    $f->delete();
                } catch (Exception $e) {
                    $message = "Unable to delete file " . $f->__toString() . ": " . $e->getMessage();
                    if ($this->failonerror) {
                        throw new BuildException($message);
                    } else {
                        $this->log($message, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
                    }
                }

            }
        }

        if (count($dirs) > 0 && $this->includeEmpty) {
            $dirCount = 0;
            for ($j = count($dirs) - 1; $j >= 0; --$j) {
                $dir = new PhingFile($d, $dirs[$j]);
                $dirFiles = $dir->listDir();
                if ($dirFiles === null || count($dirFiles) === 0) {
                    $this->log("Deleting " . $dir->__toString(), $this->verbosity);
                    try {
                        $dir->delete();
                        $dirCount++;
                    } catch (Exception $e) {
                        $message = "Unable to delete directory " . $dir->__toString();
                        if ($this->failonerror) {
                            throw new BuildException($message);
                        } else {
                            $this->log($message, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
                        }
                    }
                }
            }
            if ($dirCount > 0) {
                $this->log("Deleted $dirCount director" . ($dirCount == 1 ? "y" : "ies") . " from " . $d->__toString());
            }
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/Diagnostics.php';
require_once 'phing/system/io/PrintStream.php';
require_once 'phing/Phing.php';

/**
 * This is a task that hands off work to the Diagnostics module.
 * It lets you run diagnostics in an IDE.
 *
 * @author   Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package  phing.tasks.system
 */
class DiagnosticsTask extends Task
{
    /**
     * Execute the task.
     * This delegates to the Diagnostics class.
     * @throws BuildException on error.
     */
    public function main()
    {
        Diagnostics::doReport(new PrintStream(Phing::getOutputStream()));
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/system/io/PhingFile.php';
require_once 'phing/BuildException.php';

/**
 * Determines the directory name of the specified file.
 *
 * This task can accept the following attributes:
 * <ul>
 * <li>file
 * <li>property
 * </ul>
 * Both <b>file</b> and <b>property</b> are required.
 * <p>
 * When this task executes, it will set the specified property to the
 * value of the specified file up to, but not including, the last path
 * element. If file is a file, the directory will be the current
 * directory.
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing.tasks.system
 */
class Dirname extends Task
{
    /** @var PhingFile $file */
    private $file;
    private $property;

    /**
     * Path to take the dirname of.
     * @param string|PhingFile file a <code>File</code> value
     */
    public function setFile($file)
    {
        if ($file instanceof PhingFile) {
            $this->file = $file;
        } else {
            $this->file = new PhingFile($file);
        }
    }

    /**
     * The name of the property to set.
     * @param string $property the name of the property
     */
    public function setProperty($property)
    {
        $this->property = $property;
    }

    /**
     * Execute this task.
     * @throws BuildException on error
     */
    public function main()
    {
        if ($this->property == null) {
            throw new BuildException("property attribute required", $this->getLocation());
        }
        if ($this->file == null) {
            throw new BuildException("file attribute required", $this->getLocation());
        } else {
            $value = $this->file->getAbsoluteFile()->getParent();
            $this->getProject()->setNewProperty($this->property, $value);
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 *  Displays all the current properties in the build. The output can be sent to
 *  a file if desired.
 *
 *  Attribute "destfile" defines a file to send the properties to. This can be
 *  processed as a standard property file later.
 *
 *  Attribute "prefix" defines a prefix which is used to filter the properties
 *  only those properties starting with this prefix will be echoed.
 *
 *  By default, the "failonerror" attribute is enabled. If an error occurs while
 *  writing the properties to a file, and this attribute is enabled, then a
 *  BuildException will be thrown. If disabled, then IO errors will be reported
 *  as a log statement, but no error will be thrown.
 *
 *  Examples: <pre>
 *  &lt;echoproperties  /&gt;
 * </pre> Report the current properties to the log.
 *
 *  <pre>
 *  &lt;echoproperties destfile="my.properties" /&gt;
 * </pre> Report the current properties to the file "my.properties", and will
 *  fail the build if the file could not be created or written to.
 *
 *  <pre>
 *  &lt;echoproperties destfile="my.properties" failonerror="false"
 *      prefix="phing" /&gt;
 * </pre> Report all properties beginning with 'phing' to the file
 *  "my.properties", and will log a message if the file could not be created or
 *  written to, but will still allow the build to continue.
 *
 * @author  Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.tasks.system
 */
class EchoProperties extends Task
{
    /**
     * the properties element.
     */
    private static $PROPERTIES = "properties";

    /**
     * the property element.
     */
    private static $PROPERTY = "property";

    /**
     * name attribute for property, testcase and testsuite elements.
     */
    private static $ATTR_NAME = "name";

    /**
     * value attribute for property elements.
     */
    private static $ATTR_VALUE = "value";
    /**
     * the input file.
     * @var PhingFile
     */
    private $inFile = null;

    /**
     * File object pointing to the output file. If this is null, then
     * we output to the project log, not to a file.
     * @var PhingFile
     */
    private $destfile = null;

    /**
     * If this is true, then errors generated during file output will become
     * build errors, and if false, then such errors will be logged, but not
     * thrown.
     * @var boolean
     */
    private $failonerror = true;

    /** @var string $format */
    private $format = "text";

    /** @var string $prefix */
    private $prefix = '';

    /** @var string $regex */
    private $regex = '';

    /**
     * Sets the input file.
     *
     * @param string|PhingFile $file the input file
     */
    public function setSrcfile($file)
    {
        if (is_string($file)) {
            $this->inFile = new PhingFile($file);
        } else {
            $this->inFile = $file;
        }
    }

    /**
     *  Set a file to store the property output.  If this is never specified,
     *  then the output will be sent to the Phing log.
     *
     * @param string|PhingFile $destfile file to store the property output
     */
    public function setDestfile($destfile)
    {
        if (is_string($destfile)) {
            $this->destfile = new PhingFile($destfile);
        } else {
            $this->destfile = $destfile;
        }
    }


    /**
     * If true, the task will fail if an error occurs writing the properties
     * file, otherwise errors are just logged.
     *
     * @param  failonerror <tt>true</tt> if IO exceptions are reported as build
     *      exceptions, or <tt>false</tt> if IO exceptions are ignored.
     */
    public function setFailOnError($failonerror)
    {
        $this->failonerror = $failonerror;
    }


    /**
     *  If the prefix is set, then only properties which start with this
     *  prefix string will be recorded. If regex is not set and  if this
     *  is never set, or it is set to an empty string or <tt>null</tt>,
     *  then all properties will be recorded. <P>
     *
     *  For example, if the attribute is set as:
     *    <PRE>&lt;echoproperties  prefix="phing." /&gt;</PRE>
     *  then the property "phing.home" will be recorded, but "phing-example"
     *  will not.
     *
     * @param string $prefix The new prefix value
     */
    public function setPrefix($prefix)
    {
        if ($prefix != null && strlen($prefix) != 0) {
            $this->prefix = $prefix;
        }
    }

    /**
     *  If the regex is set, then only properties whose names match it
     *  will be recorded.  If prefix is not set and if this is never set,
     *  or it is set to an empty string or <tt>null</tt>, then all
     *  properties will be recorded.<P>
     *
     *  For example, if the attribute is set as:
     *    <PRE>&lt;echoproperties  prefix=".*phing.*" /&gt;</PRE>
     *  then the properties "phing.home" and "user.phing" will be recorded,
     *  but "phing-example" will not.
     *
     * @param string $regex The new regex value
     */
    public function setRegex($regex)
    {
        if ($regex != null && strlen($regex) != 0) {
            $this->regex = $regex;
        }
    }

    /**
     * Set the output format - xml or text.
     * @param string $ea an enumerated <code>FormatAttribute</code> value
     */
    public function setFormat($ea)
    {
        $this->format = $ea;
    }

    /**
     *  Run the task.
     *
     * @throws BuildException  trouble, probably file IO
     */
    public function main()
    {
        if ($this->prefix != null && $this->regex != null) {
            throw new BuildException("Please specify either prefix or regex, but not both", $this->getLocation());
        }

        //copy the properties file
        $allProps = array();

        /* load properties from file if specified, otherwise use Phing's properties */
        if ($this->inFile == null) {
            // add phing properties
            $allProps = $this->getProject()->getProperties();
        } elseif ($this->inFile != null) {
            if ($this->inFile->exists() && $this->inFile->isDirectory()) {
                $message = "srcfile is a directory!";
                $this->failOnErrorAction(null, $message, Project::MSG_ERR);
                return;
            }

            if ($this->inFile->exists() && !$this->inFile->canRead()) {
                $message = "Can not read from the specified srcfile!";
                $this->failOnErrorAction(null, $message, Project::MSG_ERR);
                return;
            }

            try {
                $props = new Properties();
                $props->load(new PhingFile($this->inFile));
                $allProps = $props->getProperties();
            } catch (IOException $ioe) {
                $message = "Could not read file " . $this->inFile->getAbsolutePath();
                $this->failOnErrorAction($ioe, $message, Project::MSG_WARN);
                return;
            }
        }

        $os = null;
        try {
            if ($this->destfile == null) {
                $os = Phing::getOutputStream();
                $this->saveProperties($allProps, $os);
                $this->log($os, Project::MSG_INFO);
            } else {
                if ($this->destfile->exists() && $this->destfile->isDirectory()) {
                    $message = "destfile is a directory!";
                    $this->failOnErrorAction(null, $message, Project::MSG_ERR);
                    return;
                }

                if ($this->destfile->exists() && !$this->destfile->canWrite()) {
                    $message = "Can not write to the specified destfile!";
                    $this->failOnErrorAction(null, $message, Project::MSG_ERR);
                    return;
                }
                $os = new FileOutputStream($this->destfile);
                $this->saveProperties($allProps, $os);
            }
        } catch (IOException $ioe) {
            $this->failOnErrorAction($ioe);
        }
    }

    /**
     * @param Exception $exception
     * @param string $message
     * @param int $level
     * @throws BuildException
     */
    private function failOnErrorAction(Exception $exception = null, $message = '', $level = Project::MSG_INFO)
    {
        if ($this->failonerror) {
            throw new BuildException(
                $exception !== null ? $exception : $message,
                $this->getLocation()
            );
        } else {
            $this->log(
                $exception !== null && $message === ''
                    ? $exception->getMessage()
                    : $message,
                $level
            );
        }
    }

    /**
     *  Send the key/value pairs in the hashtable to the given output stream.
     *  Only those properties matching the <tt>prefix</tt> constraint will be
     *  sent to the output stream.
     *  The output stream will be closed when this method returns.
     *
     * @param  array $allProps propfile to save
     * @param  OutputStream $os output stream
     * @throws IOException      on output errors
     * @throws BuildException   on other errors
     */
    protected function saveProperties($allProps, $os)
    {
        ksort($allProps);
        $props = new Properties();

        if ($this->regex !== '') {
            $a = new ArrayIterator($allProps);
            $i = new RegexIterator($a, $this->regex, RegexIterator::MATCH, RegexIterator::USE_KEY);
            $allProps = iterator_to_array($i);
        }
        if ($this->prefix !== '') {
            $a = new ArrayIterator($allProps);
            $i = new RegexIterator(
                $a,
                '~^' . preg_quote($this->prefix, '~') . '.*~',
                RegexIterator::MATCH,
                RegexIterator::USE_KEY
            );
            $allProps = iterator_to_array($i);
        }

        foreach ($allProps as $name => $value) {
            $props->setProperty($name, $value);
        }

        if ($this->format === "text") {
            $this->textSaveProperties($props, $os, "Phing properties");
        } elseif ($this->format === "xml") {
            $this->xmlSaveProperties($props, $os);
        }
    }

    /**
     * Output the properties as xml output.
     * @param Properties $props the properties to save
     * @param OutputStream $os the output stream to write to (Note this gets closed)
     * @throws BuildException
     */
    protected function xmlSaveProperties(Properties $props, OutputStream $os)
    {
        $doc = new DOMDocument('1.0', 'UTF-8');
        $doc->formatOutput = true;
        $rootElement = $doc->createElement(self::$PROPERTIES);

        $properties = $props->getProperties();
        ksort($properties);
        foreach ($properties as $key => $value) {
            $propElement = $doc->createElement(self::$PROPERTY);
            $propElement->setAttribute(self::$ATTR_NAME, $key);
            $propElement->setAttribute(self::$ATTR_VALUE, $value);
            $rootElement->appendChild($propElement);
        }

        try {
            $doc->appendChild($rootElement);
            $os->write($doc->saveXML());
        } catch (IOException $ioe) {
            throw new BuildException("Unable to write XML file", $ioe);
        }
    }

    /**
     * @param Properties $props the properties to record
     * @param OutputStream $os record the properties to this output stream
     * @param string $header prepend this header to the property output
     * @throws BuildException on an I/O error during a write.
     */
    protected function textSaveProperties(Properties $props, OutputStream $os, $header)
    {
        try {
            $props->storeOutputStream($os, $header);
        } catch (IOException $ioe) {
            throw new BuildException($ioe, $this->getLocation());
        }
    }
}
<?php
/*
 *  $Id: c6629f7d06eb05a6f89fe92917286e370512e787 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/Task.php';

/**
 * Echos a message to the logging system or to a file
 *
 * @author   Michiel Rook <mrook@php.net>
 * @author   Andreas Aderhold, andi@binarycloud.com
 * @version  $Id: c6629f7d06eb05a6f89fe92917286e370512e787 $
 * @package  phing.tasks.system
 */
class EchoTask extends Task
{

    protected $msg = "";

    protected $file = "";

    protected $append = false;

    protected $level = "info";

    protected $filesets = array();

    public function main()
    {
        switch ($this->level) {
            case "error":
                $loglevel = Project::MSG_ERR;
                break;
            case "warning":
                $loglevel = Project::MSG_WARN;
                break;
            case "info":
                $loglevel = Project::MSG_INFO;
                break;
            case "verbose":
                $loglevel = Project::MSG_VERBOSE;
                break;
            case "debug":
                $loglevel = Project::MSG_DEBUG;
                break;
        }

        if (count($this->filesets)) {
            if (trim(substr($this->msg, -1)) != '') {
                $this->msg .= "\n";
            }
            $this->msg .= $this->getFilesetsMsg();
        }

        if (empty($this->file)) {
            $this->log($this->msg, $loglevel);
        } else {
            if ($this->append) {
                $handle = fopen($this->file, "a");
            } else {
                $handle = fopen($this->file, "w");
            }

            fwrite($handle, $this->msg);

            fclose($handle);
        }
    }

    /**
     * Merges all filesets into a string to be echoed out
     *
     * @return string String to echo
     */
    protected function getFilesetsMsg()
    {
        $project = $this->getProject();
        $msg = '';
        foreach ($this->filesets as $fs) {
            $ds = $fs->getDirectoryScanner($project);
            $fromDir = $fs->getDir($project);
            $srcDirs = $ds->getIncludedDirectories();
            $srcFiles = $ds->getIncludedFiles();
            $msg .= 'Directory: ' . $fromDir . ' => '
                . realpath($fromDir) . "\n";
            foreach ($srcDirs as $dir) {
                $relPath = $fromDir . DIRECTORY_SEPARATOR . $dir;
                $msg .= $relPath . "\n";
            }
            foreach ($srcFiles as $file) {
                $relPath = $fromDir . DIRECTORY_SEPARATOR . $file;
                $msg .= $relPath . "\n";
            }
        }

        return $msg;
    }

    /** setter for file
     * @param $file
     */
    public function setFile($file)
    {
        $this->file = (string) $file;
    }

    /** setter for level
     * @param $level
     */
    public function setLevel($level)
    {
        $this->level = (string) $level;
    }

    /** setter for append
     * @param $append
     */
    public function setAppend($append)
    {
        $this->append = $append;
    }

    /** setter for message
     * @param $msg
     */
    public function setMsg($msg)
    {
        $this->setMessage($msg);
    }

    /** alias setter
     * @param $msg
     */
    public function setMessage($msg)
    {
        $this->msg = (string) $msg;
    }

    /** Supporting the <echo>Message</echo> syntax.
     * @param $msg
     */
    public function addText($msg)
    {
        $this->msg = (string) $msg;
    }

    /**
     * Adds a fileset to echo the files of
     *
     * @param FileSet $fs Set of files to echo
     *
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }
}
<?php
/**
 *  $Id: a85845d6c6841c7b90d9d2a4689134f88ec69d4e $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Executes a command on the shell.
 *
 * @author  Andreas Aderhold <andi@binarycloud.com>
 * @author  Hans Lellelid <hans@xmpl.org>
 * @author  Christian Weiske <cweiske@cweiske.de>
 * @version $Id: a85845d6c6841c7b90d9d2a4689134f88ec69d4e $
 * @package phing.tasks.system
 */
class ExecTask extends Task
{
    const INVALID = PHP_INT_MAX;

    private $exitValue = self::INVALID;

    /**
     * Command to be executed
     * @var string
     */
    protected $realCommand;

    /**
     * Given command
     * @var string
     */
    protected $command;

    /**
     * Commandline managing object
     *
     * @var Commandline
     */
    protected $commandline;

    /**
     * Working directory.
     * @var PhingFile
     */
    protected $dir;

    protected $currdir;

    /**
     * Operating system.
     * @var string
     */
    protected $os;

    /**
     * Whether to escape shell command using escapeshellcmd().
     * @var boolean
     */
    protected $escape = false;

    /**
     * Where to direct output.
     * @var PhingFile
     */
    protected $output;

    /**
     * Whether to use PHP's passthru() function instead of exec()
     * @var boolean
     */
    protected $passthru = false;

    /**
     * Whether to log returned output as MSG_INFO instead of MSG_VERBOSE
     * @var boolean
     */
    protected $logOutput = false;

    /**
     * Logging level for status messages
     * @var integer
     */
    protected $logLevel = Project::MSG_VERBOSE;

    /**
     * Where to direct error output.
     * @var PhingFile
     */
    protected $error;

    /**
     * If spawn is set then [unix] programs will redirect stdout and add '&'.
     * @var boolean
     */
    protected $spawn = false;

    /**
     * Property name to set with return value from exec call.
     *
     * @var string
     */
    protected $returnProperty;

    /**
     * Property name to set with output value from exec call.
     *
     * @var string
     */
    protected $outputProperty;

    /**
     * Whether to check the return code.
     * @var boolean
     */
    protected $checkreturn = false;

    /**
     *
     */
    public function __construct()
    {
        $this->commandline = new Commandline();
    }

    /**
     * Main method: wraps execute() command.
     *
     * @return void
     */
    public function main()
    {
        if (!$this->isApplicable()) {
            return;
        }

        $this->prepare();
        $this->buildCommand();
        list($return, $output) = $this->executeCommand();
        $this->cleanup($return, $output);
    }

    /**
     * Checks whether the command shall be executed
     *
     * @return boolean False if the exec command shall not be run
     */
    protected function isApplicable()
    {
        if ($this->os === null) {
            return true;
        }

        $myos = Phing::getProperty('os.name');
        $this->log('Myos = ' . $myos, Project::MSG_VERBOSE);

        if (strpos($this->os, $myos) !== false) {
            // this command will be executed only on the specified OS
            // OS matches
            return true;
        }

        $this->log(
            sprintf(
                'Operating system %s not found in %s',
                $myos,
                $this->os
            ),
            Project::MSG_VERBOSE
        );

        return false;
    }

    /**
     * Prepares the command building and execution, i.e.
     * changes to the specified directory.
     *
     * @throws BuildException
     * @return void
     */
    protected function prepare()
    {
        if ($this->dir === null) {
            return;
        }

        // expand any symbolic links first
        if (!$this->dir->getCanonicalFile()->isDirectory()) {
            throw new BuildException(
                "'" . (string) $this->dir . "' is not a valid directory"
            );
        }
        $this->currdir = getcwd();
        @chdir($this->dir->getPath());
    }

    /**
     * Builds the full command to execute and stores it in $command.
     *
     * @throws BuildException
     * @return void
     * @uses   $command
     */
    protected function buildCommand()
    {
        if ($this->command === null && $this->commandline->getExecutable() === null) {
            throw new BuildException(
                'ExecTask: Please provide "command" OR "executable"'
            );
        } else {
            if ($this->command === null) {
                $this->realCommand = Commandline::toString($this->commandline->getCommandline(), $this->escape);
            } else {
                if ($this->commandline->getExecutable() === null) {
                    $this->realCommand = $this->command;

                    //we need to escape the command only if it's specified directly
                    // commandline takes care of "executable" already
                    if ($this->escape == true) {
                        $this->realCommand = escapeshellcmd($this->realCommand);
                    }
                } else {
                    throw new BuildException(
                        'ExecTask: Either use "command" OR "executable"'
                    );
                }
            }
        }

        if ($this->error !== null) {
            $this->realCommand .= ' 2> ' . escapeshellarg($this->error->getPath());
            $this->log(
                "Writing error output to: " . $this->error->getPath(),
                $this->logLevel
            );
        }

        if ($this->output !== null) {
            $this->realCommand .= ' 1> ' . escapeshellarg($this->output->getPath());
            $this->log(
                "Writing standard output to: " . $this->output->getPath(),
                $this->logLevel
            );
        } elseif ($this->spawn) {
            $this->realCommand .= ' 1>/dev/null';
            $this->log("Sending output to /dev/null", $this->logLevel);
        }

        // If neither output nor error are being written to file
        // then we'll redirect error to stdout so that we can dump
        // it to screen below.

        if ($this->output === null && $this->error === null && $this->passthru === false) {
            $this->realCommand .= ' 2>&1';
        }

        // we ignore the spawn boolean for windows
        if ($this->spawn) {
            $this->realCommand .= ' &';
        }
    }

    /**
     * Executes the command and returns return code and output.
     *
     * @return array array(return code, array with output)
     */
    protected function executeCommand()
    {
        $this->log("Executing command: " . $this->realCommand, $this->logLevel);

        $output = array();
        $return = null;

        if ($this->passthru) {
            passthru($this->realCommand, $return);
        } else {
            exec($this->realCommand, $output, $return);
        }

        return array($return, $output);
    }

    /**
     * Runs all tasks after command execution:
     * - change working directory back
     * - log output
     * - verify return value
     *
     * @param integer $return Return code
     * @param array $output Array with command output
     *
     * @throws BuildException
     * @return void
     */
    protected function cleanup($return, $output)
    {
        if ($this->dir !== null) {
            @chdir($this->currdir);
        }

        $outloglevel = $this->logOutput ? Project::MSG_INFO : Project::MSG_VERBOSE;
        foreach ($output as $line) {
            $this->log($line, $outloglevel);
        }

        if ($this->returnProperty) {
            $this->project->setProperty($this->returnProperty, $return);
        }

        if ($this->outputProperty) {
            $this->project->setProperty(
                $this->outputProperty,
                implode("\n", $output)
            );
        }

        $this->setExitValue($return);

        if ($return != 0 && $this->checkreturn) {
            throw new BuildException("Task exited with code $return");
        }
    }

    /**
     * Set the exit value.
     *
     * @param int $value exit value of the process.
     */
    protected function setExitValue($value)
    {
        $this->exitValue = $value;
    }

    /**
     * Query the exit value of the process.
     *
     * @return int the exit value or self::INVALID if no exit value has
     *             been received.
     */
    public function getExitValue()
    {
        return $this->exitValue;
    }

    /**
     * The command to use.
     *
     * @param mixed $command String or string-compatible (e.g. w/ __toString()).
     *
     * @return void
     */
    public function setCommand($command)
    {
        $this->command = "" . $command;
    }

    /**
     * The executable to use.
     *
     * @param mixed $executable String or string-compatible (e.g. w/ __toString()).
     *
     * @return void
     */
    public function setExecutable($executable)
    {
        $this->commandline->setExecutable((string) $executable);
    }

    /**
     * Whether to use escapeshellcmd() to escape command.
     *
     * @param boolean $escape If the command shall be escaped or not
     *
     * @return void
     */
    public function setEscape($escape)
    {
        $this->escape = (bool) $escape;
    }

    /**
     * Specify the working directory for executing this command.
     *
     * @param PhingFile $dir Working directory
     *
     * @return void
     */
    public function setDir(PhingFile $dir)
    {
        $this->dir = $dir;
    }

    /**
     * Specify OS (or multiple OS) that must match in order to execute this command.
     *
     * @param string $os Operating system string (e.g. "Linux")
     *
     * @return void
     */
    public function setOs($os)
    {
        $this->os = (string) $os;
    }

    /**
     * File to which output should be written.
     *
     * @param PhingFile $f Output log file
     *
     * @return void
     */
    public function setOutput(PhingFile $f)
    {
        $this->output = $f;
    }

    /**
     * File to which error output should be written.
     *
     * @param PhingFile $f Error log file
     *
     * @return void
     */
    public function setError(PhingFile $f)
    {
        $this->error = $f;
    }

    /**
     * Whether to use PHP's passthru() function instead of exec()
     *
     * @param boolean $passthru If passthru shall be used
     *
     * @return void
     */
    public function setPassthru($passthru)
    {
        $this->passthru = (bool) $passthru;
    }

    /**
     * Whether to log returned output as MSG_INFO instead of MSG_VERBOSE
     *
     * @param boolean $logOutput If output shall be logged visibly
     *
     * @return void
     */
    public function setLogoutput($logOutput)
    {
        $this->logOutput = (bool) $logOutput;
    }

    /**
     * Whether to suppress all output and run in the background.
     *
     * @param boolean $spawn If the command is to be run in the background
     *
     * @return void
     */
    public function setSpawn($spawn)
    {
        $this->spawn = (bool) $spawn;
    }

    /**
     * Whether to check the return code.
     *
     * @param boolean $checkreturn If the return code shall be checked
     *
     * @return void
     */
    public function setCheckreturn($checkreturn)
    {
        $this->checkreturn = (bool) $checkreturn;
    }

    /**
     * The name of property to set to return value from exec() call.
     *
     * @param string $prop Property name
     *
     * @return void
     */
    public function setReturnProperty($prop)
    {
        $this->returnProperty = $prop;
    }

    /**
     * The name of property to set to output value from exec() call.
     *
     * @param string $prop Property name
     *
     * @return void
     */
    public function setOutputProperty($prop)
    {
        $this->outputProperty = $prop;
    }

    /**
     * Set level of log messages generated (default = verbose)
     *
     * @param string $level Log level
     *
     * @throws BuildException
     * @return void
     */
    public function setLevel($level)
    {
        switch ($level) {
            case 'error':
                $this->logLevel = Project::MSG_ERR;
                break;
            case 'warning':
                $this->logLevel = Project::MSG_WARN;
                break;
            case 'info':
                $this->logLevel = Project::MSG_INFO;
                break;
            case 'verbose':
                $this->logLevel = Project::MSG_VERBOSE;
                break;
            case 'debug':
                $this->logLevel = Project::MSG_DEBUG;
                break;
            default:
                throw new BuildException(
                    sprintf('Unknown log level "%s"', $level)
                );
        }
    }

    /**
     * Creates a nested <arg> tag.
     *
     * @return CommandlineArgument Argument object
     */
    public function createArg()
    {
        return $this->commandline->createArgument();
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/ExitStatusException.php';
require_once 'phing/tasks/system/condition/NestedCondition.php';

/**
 * Exits the active build, giving an additional message
 * if available.
 *
 * @author    Hans Lellelid <hans@xmpl.org> (Phing)
 * @author    Nico Seessle <nico@seessle.de> (Ant)
 *
 * @package   phing.tasks.system
 */
class FailTask extends Task
{
    /**
     * @var string $message
     */
    protected $message;

    /**
     * @var string
     */
    protected $ifCondition;

    /**
     * @var string
     */
    protected $unlessCondition;

    /**
     * @var NestedCondition
     */
    protected $nestedCondition;

    /**
     * @var integer
     */
    protected $status;

    /**
     * A message giving further information on why the build exited.
     *
     * @param string $value message to output
     *
     * @return void
     */
    public function setMsg($value)
    {
        $this->setMessage($value);
    }

    /**
     * A message giving further information on why the build exited.
     *
     * @param string $value message to output
     *
     * @return void
     */
    public function setMessage($value)
    {
        $this->message = $value;
    }

    /**
     * Only fail if a property of the given name exists in the current project.
     *
     * @param string $c property name
     *
     * @return void
     */
    public function setIf($c)
    {
        $this->ifCondition = $c;
    }

    /**
     * Only fail if a property of the given name does not
     * exist in the current project.
     *
     * @param string $c property name
     *
     * @return void
     */
    public function setUnless($c)
    {
        $this->unlessCondition = $c;
    }

    /**
     * Set the status code to associate with the thrown Exception.
     * @param int $int the <code>int</code> status
     */
    public function setStatus($int)
    {
        $this->status = (int) $int;
    }

    /**
     * {@inheritdoc}
     *
     * @return void
     *
     * @throws BuildException
     */
    public function main()
    {
        $fail =  $this->nestedConditionPresent() ? $this->testNestedCondition() :
            $this->testIfCondition() && $this->testUnlessCondition();

        if ($fail) {
            $text = null;
            if ($this->message !== null && strlen(trim($this->message)) > 0) {
                $text = trim($this->message);
            } else {
                if ($this->ifCondition !== null && $this->ifCondition !== "" && $this->testIfCondition()) {
                    $text = "if=" . $this->ifCondition;
                }
                if ($this->unlessCondition !== null && $this->unlessCondition !== "" && $this->testUnlessCondition()) {
                    if ($text === null) {
                        $text = "";
                    } else {
                        $text .= " and ";
                    }
                    $text .= "unless=" . $this->unlessCondition;
                }
                if ($this->nestedConditionPresent()) {
                    $text = "condition satisfied";
                } else {
                    if ($text === null) {
                        $text = "No message";
                    }
                }
            }

            $this->log("failing due to " . $text, Project::MSG_DEBUG);
            if ($this->status === null) {
                throw new BuildException($text);
            }

            throw new ExitStatusException($text, $this->status);
        }
    }

    /**
     * Add a condition element.
     * @return NestedCondition
     * @throws BuildException
     */
    public function createCondition()
    {
        if ($this->nestedCondition !== null) {
            throw new BuildException("Only one nested condition is allowed.");
        }
        $this->nestedCondition = new NestedCondition();
        return $this->nestedCondition;
    }

    /**
     * Set a multiline message.
     *
     * @param string $msg
     *
     * @return void
     */
    public function addText($msg)
    {
        if ($this->message === null) {
            $this->message = "";
        }
        $this->message .= $this->project->replaceProperties($msg);
    }

    /**
     * @return boolean
     */
    protected function testIfCondition()
    {
        if ($this->ifCondition === null || $this->ifCondition === "") {
            return true;
        }

        return $this->project->getProperty($this->ifCondition) !== null;
    }

    /**
     * @return boolean
     */
    protected function testUnlessCondition()
    {
        if ($this->unlessCondition === null || $this->unlessCondition === "") {
            return true;
        }

        return $this->project->getProperty($this->unlessCondition) === null;
    }

    /**
     * test the nested condition
     * @return bool true if there is none, or it evaluates to true
     * @throws BuildException
     */
    private function testNestedCondition()
    {
        $result = $this->nestedConditionPresent();

        if ($result && $this->ifCondition !== null || $this->unlessCondition !== null) {
            throw new BuildException("Nested conditions not permitted in conjunction with if/unless attributes");
        }

        return $result && $this->nestedCondition->evaluate();
    }

    /**
     * test whether there is a nested condition.
     * @return boolean
     */
    private function nestedConditionPresent()
    {
        return (bool) $this->nestedCondition;
    }
}
<?php
/*
 *  $Id: a2dfd033706d8fce23af428049b9be0875614da4 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/system/io/FileSystem.php';
include_once 'phing/mappers/FileNameMapper.php';
include_once 'phing/tasks/system/PhingTask.php';

/**
 * <foreach> task
 *
 * Task definition for the foreach task.  This task takes a list with
 * delimited values, and executes a target with set param.
 *
 * Usage:
 * <foreach list="values" target="targ" param="name" delimiter="|" />
 *
 * Attributes:
 * list      --> The list of values to process, with the delimiter character,
 *               indicated by the "delimiter" attribute, separating each value.
 * target    --> The target to call for each token, passing the token as the
 *               parameter with the name indicated by the "param" attribute.
 * param     --> The name of the parameter to pass the tokens in as to the
 *               target.
 * delimiter --> The delimiter string that separates the values in the "list"
 *               parameter.  The default is ",".
 *
 * @author    Jason Hines <jason@greenhell.com>
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: a2dfd033706d8fce23af428049b9be0875614da4 $
 * @package   phing.tasks.system
 */
class ForeachTask extends Task
{

    /** Delimter-separated list of values to process. */
    private $list;

    /** Name of parameter to pass to callee */
    private $param;

    /** Name of absolute path parameter to pass to callee */
    private $absparam;

    /** Delimiter that separates items in $list */
    private $delimiter = ',';

    /**
     * PhingCallTask that will be invoked w/ calleeTarget.
     * @var PhingCallTask
     */
    private $callee;

    /** Array of filesets */
    private $filesets = array();

    /** Instance of mapper **/
    private $mapperElement;

    /**
     * Array of filelists
     * @var array
     */
    private $filelists = array();

    /**
     * Target to execute.
     * @var string
     */
    private $calleeTarget;

    /**
     * Total number of files processed
     * @var integer
     */
    private $total_files = 0;

    /**
     * Total number of directories processed
     * @var integer
     */
    private $total_dirs = 0;

    public function init()
    {
        $this->callee = $this->project->createTask("phingcall");
        $this->callee->setOwningTarget($this->getOwningTarget());
        $this->callee->setTaskName($this->getTaskName());
        $this->callee->setLocation($this->getLocation());
        $this->callee->init();
    }

    /**
     * This method does the work.
     * @throws BuildException
     * @return void
     */
    public function main()
    {
        if ($this->list === null && count($this->filesets) == 0 && count($this->filelists) == 0) {
            throw new BuildException("Need either list, nested fileset or nested filelist to iterate through");
        }
        if ($this->param === null) {
            throw new BuildException("You must supply a property name to set on each iteration in param");
        }
        if ($this->calleeTarget === null) {
            throw new BuildException("You must supply a target to perform");
        }

        $callee = $this->callee;
        $callee->setTarget($this->calleeTarget);
        $callee->setInheritAll(true);
        $callee->setInheritRefs(true);
        $mapper = null;

        if ($this->mapperElement !== null) {
            $mapper = $this->mapperElement->getImplementation();
        }

        if (trim($this->list)) {
            $arr = explode($this->delimiter, $this->list);
            $total_entries = 0;

            foreach ($arr as $value) {
                $value = trim($value);
                $premapped = '';
                if ($mapper !== null) {
                    $premapped = $value;
                    $value = $mapper->main($value);
                    if ($value === null) {
                        continue;
                    }
                    $value = array_shift($value);
                }
                $this->log(
                    "Setting param '$this->param' to value '$value'" . ($premapped ? " (mapped from '$premapped')" : ''),
                    Project::MSG_VERBOSE
                );
                $prop = $callee->createProperty();
                $prop->setOverride(true);
                $prop->setName($this->param);
                $prop->setValue($value);
                $callee->main();
                $total_entries++;
            }
        }

        // filelists
        foreach ($this->filelists as $fl) {
            $srcFiles = $fl->getFiles($this->project);

            $this->process($callee, $fl->getDir($this->project), $srcFiles, array());
        }

        // filesets
        foreach ($this->filesets as $fs) {
            $ds = $fs->getDirectoryScanner($this->project);
            $srcFiles = $ds->getIncludedFiles();
            $srcDirs = $ds->getIncludedDirectories();

            $this->process($callee, $fs->getDir($this->project), $srcFiles, $srcDirs);
        }

        if ($this->list === null) {
            $this->log(
                "Processed {$this->total_dirs} directories and {$this->total_files} files",
                Project::MSG_VERBOSE
            );
        } else {
            $this->log(
                "Processed $total_entries entr" . ($total_entries > 1 ? 'ies' : 'y') . " in list",
                Project::MSG_VERBOSE
            );
        }
    }

    /**
     * Processes a list of files & directories
     *
     * @param Task      $callee
     * @param PhingFile $fromDir
     * @param array     $srcFiles
     * @param array     $srcDirs
     */
    protected function process(Task $callee, PhingFile $fromDir, $srcFiles, $srcDirs)
    {
        $mapper = null;

        if ($this->mapperElement !== null) {
            $mapper = $this->mapperElement->getImplementation();
        }

        $filecount = count($srcFiles);
        $this->total_files += $filecount;

        for ($j = 0; $j < $filecount; $j++) {
            $value = $srcFiles[$j];
            $premapped = "";

            if ($this->absparam) {
                $prop = $callee->createProperty();
                $prop->setOverride(true);
                $prop->setName($this->absparam);
                $prop->setValue($fromDir . FileSystem::getFileSystem()->getSeparator() . $value);
            }

            if ($mapper !== null) {
                $premapped = $value;
                $value = $mapper->main($value);
                if ($value === null) {
                    continue;
                }
                $value = array_shift($value);
            }

            if ($this->param) {
                $this->log(
                    "Setting param '$this->param' to value '$value'" . ($premapped ? " (mapped from '$premapped')" : ''),
                    Project::MSG_VERBOSE
                );
                $prop = $callee->createProperty();
                $prop->setOverride(true);
                $prop->setName($this->param);
                $prop->setValue($value);
            }

            $callee->main();
        }

        $dircount = count($srcDirs);
        $this->total_dirs += $dircount;

        for ($j = 0; $j < $dircount; $j++) {
            $value = $srcDirs[$j];
            $premapped = "";

            if ($this->absparam) {
                $prop = $callee->createProperty();
                $prop->setOverride(true);
                $prop->setName($this->absparam);
                $prop->setValue($fromDir . FileSystem::getFileSystem()->getSeparator() . $value);
            }

            if ($mapper !== null) {
                $premapped = $value;
                $value = $mapper->main($value);
                if ($value === null) {
                    continue;
                }
                $value = array_shift($value);
            }

            if ($this->param) {
                $this->log(
                    "Setting param '$this->param' to value '$value'" . ($premapped ? " (mapped from '$premapped')" : ''),
                    Project::MSG_VERBOSE
                );
                $prop = $callee->createProperty();
                $prop->setOverride(true);
                $prop->setName($this->param);
                $prop->setValue($value);
            }

            $callee->main();
        }
    }

    /**
     * @param $list
     */
    public function setList($list)
    {
        $this->list = (string) $list;
    }

    /**
     * @param $target
     */
    public function setTarget($target)
    {
        $this->calleeTarget = (string) $target;
    }

    /**
     * @param $param
     */
    public function setParam($param)
    {
        $this->param = (string) $param;
    }

    /**
     * @param $absparam
     */
    public function setAbsparam($absparam)
    {
        $this->absparam = (string) $absparam;
    }

    /**
     * @param $delimiter
     */
    public function setDelimiter($delimiter)
    {
        $this->delimiter = (string) $delimiter;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Nested creator, creates one Mapper for this task
     *
     * @return object         The created Mapper type object
     * @throws BuildException
     */
    public function createMapper()
    {
        if ($this->mapperElement !== null) {
            throw new BuildException("Cannot define more than one mapper", $this->location);
        }
        $this->mapperElement = new Mapper($this->project);

        return $this->mapperElement;
    }

    /**
     * @return SonarProperty
     */
    public function createProperty()
    {
        return $this->callee->createProperty();
    }

    /**
     * Supports embedded <filelist> element.
     * @return FileList
     */
    public function createFileList()
    {
        $num = array_push($this->filelists, new FileList());

        return $this->filelists[$num - 1];
    }
}
<?php

/*
 *  $Id: 92f075809a5c7900b630f1dbb6fe4b544d66279d $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/condition/ConditionBase.php';
require_once 'phing/tasks/system/SequentialTask.php';

/**
 * Perform some tasks based on whether a given condition holds true or
 * not.
 *
 * <p>This task is heavily based on the Condition framework that can
 * be found in Ant 1.4 and later, therefore it cannot be used in
 * conjunction with versions of Ant prior to 1.4.</p>
 *
 * <p>This task doesn't have any attributes, the condition to test is
 * specified by a nested element - see the documentation of your
 * <code><condition&gt;</code> task (see
 * <a href="http://jakarta.apache.org/ant/manual/CoreTasks/condition.html">the
 * online documentation</a> for example) for a complete list of nested
 * elements.</p>
 *
 * <p>Just like the <code><condition&gt;</code> task, only a single
 * condition can be specified - you combine them using
 * <code><and&gt;</code> or <code><or&gt;</code> conditions.</p>
 *
 * <p>In addition to the condition, you can specify three different
 * child elements, <code><elseif&gt;</code>, <code><then&gt;</code> and
 * <code><else&gt;</code>.  All three subelements are optional.
 *
 * Both <code><then&gt;</code> and <code><else&gt;</code> must not be
 * used more than once inside the if task.  Both are
 * containers for Ant tasks, just like Ant's
 * <code><parallel&gt;</code> and <code><sequential&gt;</code>
 * tasks - in fact they are implemented using the same class as Ant's
 * <code><sequential&gt;</code> task.</p>
 *
 *  The <code><elseif&gt;</code> behaves exactly like an <code><if&gt;</code>
 * except that it cannot contain the <code><else&gt;</code> element
 * inside of it.  You may specify as may of these as you like, and the
 * order they are specified is the order they are evaluated in.  If the
 * condition on the <code><if&gt;</code> is false, then the first
 * <code><elseif&gt;</code> who's conditional evaluates to true
 * will be executed.  The <code><else&gt;</code> will be executed
 * only if the <code><if&gt;</code> and all <code><elseif&gt;</code>
 * conditions are false.
 *
 * <p>Use the following task to define the <code><if&gt;</code>
 * task before you use it the first time:</p>
 *
 * <pre><code>
 *   &lt;taskdef name=&quot;if&quot; classname=&quot;net.sf.antcontrib.logic.IfTask&quot; /&gt;
 * </code></pre>
 *
 * <h3>Crude Example</h3>
 *
 * <code>
 * <if>
 *  <equals arg1="${foo}" arg2="bar" />
 *  <then>
 *    <echo message="The value of property foo is bar" />
 *  </then>
 *  <else>
 *    <echo message="The value of property foo is not bar" />
 *  </else>
 * </if>
 * </code>
 *
 * <code>
 * <if>
 *  <equals arg1="${foo}" arg2="bar" /&gt;
 *  <then>
 *   <echo message="The value of property foo is 'bar'" />
 *  </then>
 *
 *  <elseif>
 *   <equals arg1="${foo}" arg2="foo" />
 *   <then>
 *    <echo message="The value of property foo is 'foo'" />
 *   </then>
 *  </elseif>
 *
 *  <else>
 *   <echo message="The value of property foo is not 'foo' or 'bar'" />
 *  </else>
 * </if>
 * </code>
 *
 * @author <a href="mailto:stefan.bodewig@freenet.de">Stefan Bodewig</a>
 * @package phing.tasks.system
 */
class IfTask extends ConditionBase
{

    private $thenTasks = null;
    private $elseIfTasks = array();
    private $elseTasks = null;

    /***
     * A nested Else if task
     * @param ElseIfTask $ei
     */
    public function addElseIf(ElseIfTask $ei)
    {
        $this->elseIfTasks[] = $ei;
    }

    /**
     * A nested <then> element - a container of tasks that will
     * be run if the condition holds true.
     *
     * <p>Not required.</p>
     * @param SequentialTask $t
     * @throws BuildException
     */
    public function addThen(SequentialTask $t)
    {
        if ($this->thenTasks != null) {
            throw new BuildException("You must not nest more than one <then> into <if>");
        }
        $this->thenTasks = $t;
    }

    /**
     * A nested <else> element - a container of tasks that will
     * be run if the condition doesn't hold true.
     *
     * <p>Not required.</p>
     * @param SequentialTask $e
     * @throws BuildException
     */
    public function addElse(SequentialTask $e)
    {
        if ($this->elseTasks != null) {
            throw new BuildException("You must not nest more than one <else> into <if>");
        }
        $this->elseTasks = $e;
    }

    public function main()
    {

        if ($this->countConditions() > 1) {
            throw new BuildException("You must not nest more than one condition into <if>");
        }
        if ($this->countConditions() < 1) {
            throw new BuildException("You must nest a condition into <if>");
        }
        $conditions = $this->getConditions();
        $c = $conditions[0];

        if ($c->evaluate()) {
            if ($this->thenTasks != null) {
                $this->thenTasks->main();
            }
        } else {
            $done = false;
            $sz = count($this->elseIfTasks);
            for ($i = 0; $i < $sz && !$done; $i++) {
                $ei = $this->elseIfTasks[$i];
                if ($ei->evaluate()) {
                    $done = true;
                    $ei->main();
                }
            }

            if (!$done && $this->elseTasks != null) {
                $this->elseTasks->main();
            }
        }
    }
}

/**
 * "Inner" class for IfTask.
 * This class has same basic structure as the IfTask, although of course it doesn't support <else> tags.
 *
 * @package phing.tasks.system
 */
class ElseIfTask extends ConditionBase
{

    private $thenTasks = null;

    /**
     * @param SequentialTask $t
     * @throws BuildException
     */
    public function addThen(SequentialTask $t)
    {
        if ($this->thenTasks != null) {
            throw new BuildException("You must not nest more than one <then> into <elseif>");
        }
        $this->thenTasks = $t;
    }

    /**
     * @throws BuildException
     * @return boolean
     */
    public function evaluate()
    {

        if ($this->countConditions() > 1) {
            throw new BuildException("You must not nest more than one condition into <elseif>");
        }
        if ($this->countConditions() < 1) {
            throw new BuildException("You must nest a condition into <elseif>");
        }

        $conditions = $this->getConditions();
        $c = $conditions[0];

        return $c->evaluate();
    }

    /**
     *
     */
    public function main()
    {
        if ($this->thenTasks != null) {
            $this->thenTasks->main();
        }
    }
}
<?php
/*
 *  $Id: a3cbb4362c17c028400e48e62ea3e2caee4a3482 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/system/io/FileSystem.php';
require_once 'phing/system/io/PhingFile.php';
require_once 'phing/parser/ProjectConfigurator.php';

/**
 * Imports another build file into the current project.
 *
 * Targets and properties of the imported file can be overrridden
 * by targets and properties of the same name declared in the importing file.
 *
 * The imported file will have a new synthetic property of
 * "phing.file.<projectname>" declared which gives the full path to the
 * imported file. Additionally each target in the imported file will be
 * declared twice: once with the normal name and once with "<projectname>."
 * prepended. The "<projectname>.<targetname>" synthetic targets allow the
 * importing file a mechanism to call the imported files targets as
 * dependencies or via the <phing> or <phingcall> task mechanisms.
 *
 * @author Bryan Davis <bpd@keynetics.com>
 * @version $Id: a3cbb4362c17c028400e48e62ea3e2caee4a3482 $
 * @package phing.tasks.system
 */
class ImportTask extends Task
{

    /**
     * @var FileSystem
     */
    protected $fs;

    /**
     * @var PhingFile
     */
    protected $file = null;

    /**
     * @var array
     */
    private $filesets = array();

    /**
     * @var bool
     */
    protected $optional = false;

    /**
     * Initialize task.
     * @return void
     */
    public function init()
    {
        $this->fs = FileSystem::getFileSystem();
    } //end init

    /**
     * Set the file to import.
     * @param  string $f Path to file
     * @return void
     */
    public function setFile($f)
    {
        $this->file = $f;
    }

    /**
     * Nested creator, adds a set of files (nested <fileset> attribute).
     * This is for when you don't care what order files get appended.
     * @return FileSet
     */
    public function createFileSet()
    {
        $num = array_push($this->filesets, new FileSet());
        return $this->filesets[$num-1];
    }

    /**
     * Is this include optional?
     * @param  bool $opt If true, do not stop the build if the file does not
     *                   exist
     * @return void
     */
    public function setOptional($opt)
    {
        $this->optional = $opt;
    }

    /**
     * Parse a Phing build file and copy the properties, tasks, data types and
     * targets it defines into the current project.
     *
     * @throws BuildException
     * @return void
     */
    public function main()
    {
        if ($this->getOwningTarget() == null || $this->getOwningTarget()->getName() != '') {
            throw new BuildException("import only allowed as a top-level task");
        }

        // Single file.
        if ($this->file !== null) {
            $file = new PhingFile($this->file);
            if (!$file->isAbsolute()) {
                $file = new PhingFile($this->project->getBasedir(), $this->file);
            }
            if (!$file->exists()) {
                $msg = "Unable to find build file: {$file->getPath()}";
                if ($this->optional) {
                    $this->log($msg . '... skipped');
                    return;
                } else {
                    throw new BuildException($msg);
                }
            }
            $this->importFile($file);
        }

        // Filesets.
        $total_files = 0;
        $total_dirs = 0;
        foreach ($this->filesets as $fs) {
            $ds = $fs->getDirectoryScanner($this->project);
            $fromDir = $fs->getDir($this->project);

            $srcFiles = $ds->getIncludedFiles();
            $srcDirs = $ds->getIncludedDirectories();

            $filecount = count($srcFiles);
            $total_files = $total_files + $filecount;
            for ($j = 0; $j < $filecount; $j++) {
                $this->importFile(new PhingFile($fromDir, $srcFiles[$j]));
            }

            $dircount = count($srcDirs);
            $total_dirs = $total_dirs + $dircount;
            for ($j = 0; $j < $dircount; $j++) {
                $this->importFile(new PhingFile($fromDir, $srcDirs[$j]));
            }
        }
    } //end main

    /**
     * Parse a Phing build file and copy the properties, tasks, data types and
     * targets it defines into the current project.
     *
     * @throws BuildException
     * @return void
     */
    protected function importFile(PhingFile $file)
    {
        $ctx = $this->project->getReference("phing.parsing.context");
        $cfg = $ctx->getConfigurator();
        // Import xml file into current project scope
        // Since this is delayed until after the importing file has been
        // processed, the properties and targets of this new file may not take
        // effect if they have alreday been defined in the outer scope.
        $this->log("Importing file from {$file->getAbsolutePath()}", Project::MSG_VERBOSE);
        ProjectConfigurator::configureProject($this->project, $file);
    } //end importFile

} //end ImportTask
<?php

/*
 * $Id: 543e5401804eda643673c4ae52d20dcfe8104833 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/types/Path.php';

/**
 * Adds a normalized path to the PHP include_path.
 *
 * This provides a way to alter the include_path without editing any global php.ini settings
 * or PHP_CLASSPATH environment variable.
 *
 * <code>
 *   <includepath classpath="new/path/here"/>
 * </code>
 *
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: 543e5401804eda643673c4ae52d20dcfe8104833 $
 * @package   phing.tasks.system
 */
class IncludePathTask extends Task
{

    /**
     * Classname of task to register.
     * This can be a dot-path -- relative to a location on PHP include_path.
     * E.g. path.to.MyClass ->  path/to/MyClass.php
     * @var string
     */
    private $classname;

    /**
     * Path to add to PHP include_path to aid in finding specified class.
     * @var Path
     */
    private $classpath;

    /**
     * Refid to already defined classpath
     */
    private $classpathId;

    /**
     * Whether to prepend, append or replace the include path
     * @var string
     */
    private $mode = "prepend";

    /**
     * Set the classpath to be used when searching for component being defined
     *
     * @param Path $classpath An Path object containing the classpath.
     */
    public function setClasspath(Path $classpath)
    {
        if ($this->classpath === null) {
            $this->classpath = $classpath;
        } else {
            $this->classpath->append($classpath);
        }
    }

    /**
     * Create the classpath to be used when searching for component being defined
     */
    public function createClasspath()
    {
        if ($this->classpath === null) {
            $this->classpath = new Path($this->project);
        }

        return $this->classpath->createPath();
    }

    /**
     * Reference to a classpath to use when loading the files.
     * @param Reference $r
     * @throws BuildException
     */
    public function setClasspathRef(Reference $r)
    {
        $this->classpathId = $r->getRefId();
        $this->createClasspath()->setRefid($r);
    }

    /**
     * @param $mode
     * @throws BuildException
     */
    public function setMode($mode)
    {
        if (!in_array($mode, array('append', 'prepend', 'replace'))) {
            throw new BuildException("Illegal mode: needs to be either append, prepend or replace");
        }

        $this->mode = $mode;
    }

    /** Main entry point */
    public function main()
    {

        // Apparently casting to (string) no longer invokes __toString() automatically.
        if (is_object($this->classpath)) {
            $classpath = $this->classpath->__toString();
        }

        if (empty($classpath)) {
            throw new BuildException("Provided classpath was empty.");
        }

        $curr_parts = Phing::explodeIncludePath();
        $add_parts = Phing::explodeIncludePath($classpath);
        $new_parts = array_diff($add_parts, $curr_parts);

        if ($new_parts) {
            $this->updateIncludePath($new_parts, $curr_parts);
        }
    }

    /**
     * @param $new_parts
     * @param $curr_parts
     */
    private function updateIncludePath($new_parts, $curr_parts)
    {
        $includePath = array();
        $verb = "";

        switch ($this->mode) {
            case "append":
                $includePath = array_merge($curr_parts, $new_parts);
                $verb = "Appending";
                break;

            case "replace":
                $includePath = $new_parts;
                $verb = "Replacing";
                break;

            case "prepend":
                $includePath = array_merge($new_parts, $curr_parts);
                $verb = "Prepending";
                break;
        }

        $this->log(
            $verb . " new include_path components: " . implode(PATH_SEPARATOR, $new_parts),
            Project::MSG_VERBOSE
        );

        set_include_path(implode(PATH_SEPARATOR, $includePath));
    }
}
<?php
/*
 *  $Id: 6bf80f75d0676da89348d0bf5682995079e67270 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/input/InputRequest.php';
include_once 'phing/input/YesNoInputRequest.php';
include_once 'phing/input/MultipleChoiceInputRequest.php';

/**
 * Reads input from the InputHandler.
 *
 * @see       Project::getInputHandler()
 * @author    Hans Lellelid <hans@xmpl.org> (Phing)
 * @author    Ulrich Schmidt <usch@usch.net> (Ant)
 * @author    Stefan Bodewig <stefan.bodewig@epost.de> (Ant)
 * @version   $Id: 6bf80f75d0676da89348d0bf5682995079e67270 $
 * @package   phing.tasks.system
 */
class InputTask extends Task
{

    private $validargs;
    private $message = ""; // required
    private $propertyName; // required
    private $defaultValue;
    private $promptChar;

    /**
     * Defines valid input parameters as comma separated strings. If set, input
     * task will reject any input not defined as accepted and requires the user
     * to reenter it. Validargs are case sensitive. If you want 'a' and 'A' to
     * be accepted you need to define both values as accepted arguments.
     *
     * @param string $validargs A comma separated String defining valid input args.
     */
    public function setValidargs($validargs)
    {
        $this->validargs = $validargs;
    }

    /**
     * Defines the name of a property to be set from input.
     *
     * @param string $name Name for the property to be set from input
     */
    public function setPropertyName($name)
    {
        $this->propertyName = $name;
    }

    /**
     * Sets the Message which gets displayed to the user during the build run.
     * @param The $message
     * @internal param The $message message to be displayed.
     */
    public function setMessage($message)
    {
        $this->message = $message;
    }

    /**
     * Set a multiline message.
     * @param $msg
     */
    public function addText($msg)
    {
        $this->message .= $this->project->replaceProperties($msg);
    }

    /**
     * Add a default value.
     * @param string $v
     */
    public function setDefaultValue($v)
    {
        $this->defaultValue = $v;
    }

    /**
     * Set the character/string to use for the prompt.
     * @param string $c
     */
    public function setPromptChar($c)
    {
        $this->promptChar = $c;
    }

    /**
     * Actual method executed by phing.
     * @throws BuildException
     */
    public function main()
    {

        if ($this->propertyName === null) {
            throw new BuildException("You must specify a value for propertyName attribute.");
        }

        if ($this->message === "") {
            throw new BuildException("You must specify a message for input task.");
        }

        if ($this->validargs !== null) {
            $accept = preg_split('/[\s,]+/', $this->validargs);

            // is it a boolean (yes/no) inputrequest?
            $yesno = false;
            if (count($accept) == 2) {
                $yesno = true;
                foreach ($accept as $ans) {
                    if (!StringHelper::isBoolean($ans)) {
                        $yesno = false;
                        break;
                    }
                }
            }
            if ($yesno) {
                $request = new YesNoInputRequest($this->message, $accept);
            } else {
                $request = new MultipleChoiceInputRequest($this->message, $accept);
            }
        } else {
            $request = new InputRequest($this->message);
        }

        // default default is curr prop value
        $request->setDefaultValue($this->project->getProperty($this->propertyName));

        $request->setPromptChar($this->promptChar);

        // unless overridden...
        if ($this->defaultValue !== null) {
            $request->setDefaultValue($this->defaultValue);
        }

        $this->project->getInputHandler()->handleInput($request);

        $value = $request->getInput();

        if ($value !== null) {
            $this->project->setUserProperty($this->propertyName, $value);
        }
    }

}
<?php
/*
 * $Id: 0615380481a0ab5811bc85df21ecfe8ffacdfb5c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */
require_once 'phing/Task.php';

/**
 * LoadFileTask
 *
 * Loads a (text) file and stores the contents in a property.
 * Supports filterchains.
 *
 * @author  Michiel Rook <mrook@php.net>
 * @version $Id: 0615380481a0ab5811bc85df21ecfe8ffacdfb5c $
 * @package phing.tasks.ext
 */
class LoadFileTask extends Task
{
    /**
     * File to read
     * @var PhingFile file
     */
    private $file;

    /**
     * Property to be set
     * @var string $property
     */
    private $property;

    /**
     * Array of FilterChain objects
     * @var FilterChain[]
     */
    private $filterChains = array();

    private $failOnError = true;

    private $quiet = false;

    /**
     * @param bool $fail
     */
    public function setFailOnError($fail)
    {
        $this->failOnError = $fail;
    }

    /**
     * @param bool $quiet
     */
    public function setQuiet($quiet)
    {
        $this->quiet = $quiet;
        if ($quiet) {
            $this->failOnError = false;
        }
    }

    /**
     * Set file to read
     * @param PhingFile $file
     */
    public function setFile($file)
    {
        $this->file = $file;
    }

    /**
     * Convenience setter to maintain Ant compatibility (@see setFile())
     * @param $srcFile
     * @internal param PhingFile $file
     */
    public function setSrcFile($srcFile)
    {
        $this->file = $srcFile;
    }

    /**
     * Set name of property to be set
     * @param $property
     * @return void
     */
    public function setProperty($property)
    {
        $this->property = $property;
    }

    /**
     * Creates a filterchain
     *
     * @return object The created filterchain object
     */
    public function createFilterChain()
    {
        $num = array_push($this->filterChains, new FilterChain($this->project));

        return $this->filterChains[$num - 1];
    }

    /**
     * Main method
     *
     * @return void
     * @throws BuildException
     */
    public function main()
    {
        if (empty($this->file)) {
            throw new BuildException("Attribute 'file' required", $this->getLocation());
        }

        if (empty($this->property)) {
            throw new BuildException("Attribute 'property' required", $this->getLocation());
        }
        if ($this->quiet && $this->failOnError) {
            throw new BuildException("quiet and failonerror cannot both be set to true");
        }

        try {
            if (is_string($this->file)) {
                $this->file = new PhingFile($this->file);
            }
            if (!$this->file->exists()) {
                $message = (string)$this->file . ' doesn\'t exist';
                if ($this->failOnError) {
                    throw new BuildException($message);
                } else {
                    $this->log($message, $this->quiet ? Project::MSG_WARN : Project::MSG_ERR);
                    return;
                }
            }

            $this->log("loading " . (string)$this->file . " into property " . $this->property, Project::MSG_VERBOSE);
            // read file (through filterchains)
            $contents = "";

            if ($this->file->length() > 0) {
                $reader = FileUtils::getChainedReader(new FileReader($this->file), $this->filterChains, $this->project);
                while (-1 !== ($buffer = $reader->read())) {
                    $contents .= $buffer;
                }
                $reader->close();
            } else {
                $this->log("Do not set property " . $this->property . " as its length is 0.",
                    $this->quiet ? Project::MSG_VERBOSE : Project::MSG_INFO);
            }

            // publish as property
            if ($contents !== '') {
                $this->project->setNewProperty($this->property, $contents);
                $this->log("loaded " . strlen($contents) . " characters", Project::MSG_VERBOSE);
                $this->log($this->property . " := " . $contents, Project::MSG_DEBUG);
            }
        } catch (IOException $ioe) {
            $message = "Unable to load resource: " . $ioe->getMessage();
            if ($this->failOnError) {
                throw new BuildException($message, $ioe, $this->getLocation());
            } else {
                $this->log($message, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_ERR);
            }
        } catch (BuildException $be) {
            if ($this->failOnError) {
                throw $be;
            } else {
                $this->log($be->getMessage(), $this->quiet ? Project::MSG_VERBOSE : Project::MSG_ERR);
            }
        }
    }
}
<?php
/*
 *  $Id: 27fef32074e38b7a6b16b5194845406917d56dde $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/types/selectors/SelectorContainer.php';
include_once 'phing/types/FileSet.php';
include_once 'phing/types/PatternSet.php';
include_once 'phing/util/DirectoryScanner.php';

/**
 * This is an abstract task that should be used by all those tasks that
 * require to include or exclude files based on pattern matching.
 *
 * This is very closely based on the ANT class of the same name.
 *
 * @author    Hans Lellelid <hans@xmpl.org> (Phing)
 * @author    Arnout J. Kuiper <ajkuiper@wxs.nl> (Ant)
 * @author    Stefano Mazzocchi  <stefano@apache.org> (Ant)
 * @author    Sam Ruby <rubys@us.ibm.com> (Ant)
 * @author    Jon S. Stevens <jon@clearink.com> (Ant
 * @author    Stefan Bodewig <stefan.bodewig@epost.de> (Ant)
 * @author    Bruce Atherton <bruce@callenish.com> (Ant)
 * @version   $Id: 27fef32074e38b7a6b16b5194845406917d56dde $
 * @package   phing.tasks.system
 */
abstract class MatchingTask extends Task implements SelectorContainer
{

    /** @var boolean */
    protected $useDefaultExcludes = true;

    /** @var FileSet */
    protected $fileset;

    /**
     * Create instance; set fileset to new FileSet.
     */
    public function __construct()
    {
        $this->fileset = new FileSet();
    }

    /**
     * @see ProjectComponent::setProject()
     * @param Project $project
     */
    public function setProject($project)
    {
        parent::setProject($project);
        $this->fileset->setProject($project);
    }

    /**
     * add a name entry on the include list
     * @return PatternSetNameEntry
     */
    public function createInclude()
    {
        return $this->fileset->createInclude();
    }

    /**
     * add a name entry on the include files list
     * @return PatternSetNameEntry
     */
    public function createIncludesFile()
    {
        return $this->fileset->createIncludesFile();
    }

    /**
     * add a name entry on the exclude list
     * @return PatternSetNameEntry
     */
    public function createExclude()
    {
        return $this->fileset->createExclude();
    }

    /**
     * add a name entry on the include files list
     * @return PatternSetNameEntry
     */
    public function createExcludesFile()
    {
        return $this->fileset->createExcludesFile();
    }

    /**
     * add a set of patterns
     * @return PatternSet
     */
    public function createPatternSet()
    {
        return $this->fileset->createPatternSet();
    }

    /**
     * Sets the set of include patterns. Patterns may be separated by a comma
     * or a space.
     *
     * @param  string $includes the string containing the include patterns
     * @return void
     */
    public function setIncludes($includes)
    {
        $this->fileset->setIncludes($includes);
    }

    /**
     * Sets the set of exclude patterns. Patterns may be separated by a comma
     * or a space.
     *
     * @param string $excludes the string containing the exclude patterns
     */
    public function setExcludes($excludes)
    {
        $this->fileset->setExcludes($excludes);
    }

    /**
     * Sets whether default exclusions should be used or not.
     *
     * @param boolean $useDefaultExcludes "true"|"on"|"yes" when default exclusions
     *                                    should be used, "false"|"off"|"no" when they
     *                                    shouldn't be used.
     */
    public function setDefaultexcludes($useDefaultExcludes)
    {
        $this->useDefaultExcludes = (boolean) $useDefaultExcludes;
    }

    /**
     * Returns the directory scanner needed to access the files to process.
     * @param PhingFile $baseDir
     * @throws BuildException
     * @return DirectoryScanner
     */
    protected function getDirectoryScanner(PhingFile $baseDir)
    {
        $this->fileset->setDir($baseDir);
        $this->fileset->setDefaultexcludes($this->useDefaultExcludes);

        return $this->fileset->getDirectoryScanner($this->project);
    }

    /**
     * Sets the name of the file containing the includes patterns.
     *
     * @param  PhingFile $includesfile A string containing the filename to fetch
     *                                 the include patterns from.
     * @return void
     */
    public function setIncludesfile(PhingFile $includesfile)
    {
        $this->fileset->setIncludesfile($includesfile);
    }

    /**
     * Sets the name of the file containing the includes patterns.
     *
     * @param  PhingFile $excludesfile A string containing the filename to fetch
     *                                 the include patterns from.
     * @return void
     */
    public function setExcludesfile(PhingFile $excludesfile)
    {
        $this->fileset->setExcludesfile($excludesfile);
    }

    /**
     * Sets case sensitivity of the file system
     *
     * @param  boolean $isCaseSensitive "true"|"on"|"yes" if file system is case
     *                                  sensitive, "false"|"off"|"no" when not.
     * @return void
     */
    public function setCaseSensitive($isCaseSensitive)
    {
        $this->fileset->setCaseSensitive($isCaseSensitive);
    }

    /**
     * Sets whether or not symbolic links should be followed.
     *
     * @param  boolean $followSymlinks whether or not symbolic links should be followed
     * @return void
     */
    public function setFollowSymlinks($followSymlinks)
    {
        $this->fileset->setFollowSymlinks($followSymlinks);
    }

    /**
     * Indicates whether there are any selectors here.
     *
     * @return boolean Whether any selectors are in this container
     */
    public function hasSelectors()
    {
        return $this->fileset->hasSelectors();
    }

    /**
     * Gives the count of the number of selectors in this container
     *
     * @return int The number of selectors in this container
     */
    public function selectorCount()
    {
        return $this->fileset->selectorCount();
    }

    /**
     * Returns the set of selectors as an array.
     *
     * @param Project $p
     * @return array FileSelector[] An array of selectors in this container
     */
    public function getSelectors(Project $p)
    {
        return $this->fileset->getSelectors($p);
    }

    /**
     * Returns an enumerator for accessing the set of selectors.
     *
     * @return an enumerator that goes through each of the selectors
     */
    public function selectorElements()
    {
        return $this->fileset->selectorElements();
    }

    /**
     * Add a new selector into this container.
     *
     * @param  FileSelector $selector the new selector to add
     * @return void
     */
    public function appendSelector(FileSelector $selector)
    {
        $this->fileset->appendSelector($selector);
    }

    /* Methods below all add specific selectors */

    /**
     * add a "Select" selector entry on the selector list
     * @return SelectSelector
     */
    public function createSelector()
    {
        return $this->fileset->createSelector();
    }

    /**
     * add an "And" selector entry on the selector list
     * @return AndSelector
     */
    public function createAnd()
    {
        return $this->fileset->createAnd();
    }

    /**
     * add an "Or" selector entry on the selector list
     * @return OrSelector
     */
    public function createOr()
    {
        return $this->fileset->createOr();
    }

    /**
     * add a "Not" selector entry on the selector list
     * @return NotSelector
     */
    public function createNot()
    {
        return $this->fileset->createNot();
    }

    /**
     * add a "None" selector entry on the selector list
     * @return NoneSelector
     */
    public function createNone()
    {
        return $this->fileset->createNone();
    }

    /**
     * add a majority selector entry on the selector list
     * @return MajoritySelector
     */
    public function createMajority()
    {
        return $this->fileset->createMajority();
    }

    /**
     * add a selector date entry on the selector list
     * @return DateSelector
     */
    public function createDate()
    {
        return $this->fileset->createDate();
    }

    /**
     * add a selector size entry on the selector list
     * @return SizeSelector
     */
    public function createSize()
    {
        return $this->fileset->createSize();
    }

    /**
     * add a selector filename entry on the selector list
     * @return FilenameSelector
     */
    public function createFilename()
    {
        return $this->fileset->createFilename();
    }

    /**
     * add an extended selector entry on the selector list
     * @return ExtendSelector
     */
    public function createCustom()
    {
        return $this->fileset->createCustom();
    }

    /**
     * add a contains selector entry on the selector list
     * @return ContainsSelector
     */
    public function createContains()
    {
        return $this->fileset->createContains();
    }

    /**
     * add a present selector entry on the selector list
     * @return PresentSelector
     */
    public function createPresent()
    {
        return $this->fileset->createPresent();
    }

    /**
     * add a depth selector entry on the selector list
     * @return DepthSelector
     */
    public function createDepth()
    {
        return $this->fileset->createDepth();
    }

    /**
     * add a depends selector entry on the selector list
     * @return DependSelector
     */
    public function createDepend()
    {
        return $this->fileset->createDepend();
    }

    /**
     * add a readable selector entry on the selector list
     * @return ReadableSelector
     */
    public function createReadable()
    {
        return $this->fileset->createReadable();
    }

    /**
     * add a writable selector entry on the selector list
     * @return WritableSelector
     */
    public function createWritable()
    {
        return $this->fileset->createWritable();
    }

    /**
     * add a different selector entry on the selector list
     * @return DifferentSelector
     */
    public function createDifferent()
    {
        return $this->fileset->createDifferent();
    }

    /**
     * Accessor for the implict fileset.
     *
     * @return FileSet
     */
    final protected function getImplicitFileSet()
    {
        return $this->fileset;
    }
}
<?php
/*
 *  $Id: e330e2db38e780989e79afe3bdbd269afe8b02fa $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/system/io/PhingFile.php';

/**
 * Task to create a directory.
 *
 * @author   Andreas Aderhold, andi@binarycloud.com
 * @version  $Id: e330e2db38e780989e79afe3bdbd269afe8b02fa $
 * @package  phing.tasks.system
 */
class MkdirTask extends Task
{

    /**
     * Directory to create.
     * @var PhingFile $dir
     */
    private $dir;

    /**
     * Mode to create directory with
     * @var integer
     */
    private $mode = 0;

    /**
     * Sets up this object internal stuff. i.e. the default mode.
     */
    public function __construct()
    {
        $this->mode = 0777 - umask();
    }

    /**
     * create the directory and all parents
     *
     * @throws BuildException if dir is somehow invalid, or creation failed.
     */
    public function main()
    {
        if ($this->dir === null) {
            throw new BuildException("dir attribute is required", $this->location);
        }
        if ($this->dir->isFile()) {
            throw new BuildException("Unable to create directory as a file already exists with that name: " . $this->dir->getAbsolutePath(
                ));
        }
        if (!$this->dir->exists()) {
            $result = $this->dir->mkdirs($this->mode);
            if (!$result) {
                if ($this->dir->exists()) {
                    $this->log("A different process or task has already created " . $this->dir->getAbsolutePath());

                    return;
                }
                $msg = "Directory " . $this->dir->getAbsolutePath(
                    ) . " creation was not successful for an unknown reason";
                throw new BuildException($msg, $this->location);
            }
            $this->log("Created dir: " . $this->dir->getAbsolutePath());
        }
    }

    /**
     * The directory to create; required.
     * @param PhingFile $dir
     * @return void
     */
    public function setDir(PhingFile $dir)
    {
        $this->dir = $dir;
    }

    /**
     * Sets mode to create directory with
     * @param mixed $mode
     * @return void
     */
    public function setMode($mode)
    {
        $this->mode = base_convert((int) $mode, 8, 10);
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/CopyTask.php';
include_once 'phing/system/io/PhingFile.php';
include_once 'phing/system/io/IOException.php';

/**
 * Moves a file or directory to a new file or directory.
 *
 * By default, the destination file is overwritten if it
 * already exists.  When overwrite is turned off, then files
 * are only moved if the source file is newer than the
 * destination file, or when the destination file does not
 * exist.
 *
 * Source files and directories are only deleted when the file or
 * directory has been copied to the destination successfully.
 *
 * @package phing.tasks.system
 */
class MoveTask extends CopyTask
{

    /**
     *
     */
    public function __construct()
    {
        parent::__construct();
        $this->forceOverwrite = true;
    }

    /**
     * Validates attributes coming in from XML
     *
     * @return void
     *
     * @throws BuildException
     */
    protected function validateAttributes()
    {
        if ($this->file !== null && $this->file->isDirectory()) {
            if (($this->destFile !== null && $this->destDir !== null)
                || ($this->destFile === null && $this->destDir === null)
            ) {
                throw new BuildException("One and only one of tofile and todir must be set.");
            }

            if ($this->destFile === null) {
                $this->destFile = new PhingFile($this->destDir, $this->file->getName());
            }

            if ($this->destDir === null) {
                $this->destDir = $this->destFile->getParentFile();
            }

            $this->completeDirMap[$this->file->getAbsolutePath()] = $this->destFile->getAbsolutePath();

            $this->file = null;
        } else {
            parent::validateAttributes();
        }
    }

    protected function doWork()
    {
        if (count($this->completeDirMap) > 0) {
            foreach ($this->completeDirMap as $from => $to) {
                $f = new PhingFile($from);
                $d = new PhingFile($to);

                $moved = false;
                try { // try to rename
                    $this->log("Attempting to rename $from to $to", $this->verbosity);
                    $this->fileUtils->copyFile(
                        $f,
                        $d,
                        $this->forceOverwrite,
                        $this->preserveLMT,
                        $this->filterChains,
                        $this->getProject(),
                        $this->mode
                    );
                    $f->delete(true);
                    $moved = true;
                } catch (IOException $ioe) {
                    $moved = false;
                    $this->logError("Failed to rename $from to $to: " . $ioe->getMessage());
                }
            }
        }

        $copyMapSize = count($this->fileCopyMap);
        if ($copyMapSize > 0) {
            // files to move
            $this->log("Moving $copyMapSize files to " . $this->destDir->getAbsolutePath());

            foreach ($this->fileCopyMap as $from => $to) {
                if ($from == $to) {
                    $this->log("Skipping self-move of $from", $this->verbosity);
                    continue;
                }

                $f = new PhingFile($from);
                $d = new PhingFile($to);

                try { // try to move
                    $this->log("Moving $from to $to", $this->verbosity);

                    $this->fileUtils->copyFile(
                        $f,
                        $d,
                        $this->forceOverwrite,
                        $this->preserveLMT,
                        $this->filterChains,
                        $this->getProject(),
                        $this->mode
                    );

                    $f->delete();
                } catch (IOException $ioe) {
                    $this->logError("Failed to move $from to $to: " . $ioe->getMessage(), $this->location);
                }
            } // foreach fileCopyMap
        } // if copyMapSize

        // handle empty dirs if appropriate
        if ($this->includeEmpty) {
            $count = 0;
            foreach ($this->dirCopyMap as $srcDir => $destDir) {
                $d = new PhingFile((string) $destDir);
                if (!$d->exists()) {
                    if (!$d->mkdirs()) {
                        $this->logError("Unable to create directory " . $d->getAbsolutePath());
                    } else {
                        $count++;
                    }
                }
            }
            if ($count > 0) {
                $this->log(
                    "moved $count empty director" . ($count == 1 ? "y" : "ies") . " to " . $this->destDir->getAbsolutePath(
                    )
                );
            }
        }

        if (count($this->filesets) > 0) {
            // process filesets
            foreach ($this->filesets as $fs) {
                $dir = $fs->getDir($this->project);
                if ($this->okToDelete($dir)) {
                    $this->deleteDir($dir);
                }
            }
        }
    }

    /**
     * Its only ok to delete a dir tree if there are no files in it.
     *
     * @param $d
     *
     * @throws IOException
     *
     * @return bool
     */
    private function okToDelete($d)
    {
        $list = $d->listDir();
        if ($list === null) {
            return false; // maybe io error?
        }

        foreach ($list as $s) {
            $f = new PhingFile($d, $s);
            if ($f->isDirectory()) {
                if (!$this->okToDelete($f)) {
                    return false;
                }
            } else {
                // found a file
                return false;
            }
        }

        return true;
    }

    /**
     * Go and delete the directory tree.
     *
     * @param $d
     *
     * @throws BuildException
     * @throws IOException
     */
    private function deleteDir($d)
    {

        $list = $d->listDir();
        if ($list === null) {
            return; // on an io error list() can return null
        }

        foreach ($list as $fname) {
            $f = new PhingFile($d, $fname);
            if ($f->isDirectory()) {
                $this->deleteDir($f);
            } else {
                throw new BuildException("UNEXPECTED ERROR - The file " . $f->getAbsolutePath() . " should not exist!");
            }
        }

        $this->log("Deleting directory " . $d->getPath(), $this->verbosity);
        try {
            $d->delete();
        } catch (Exception $e) {
            $this->logError("Unable to delete directory " . $d->__toString() . ": " . $e->getMessage());
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/types/Path.php';
include_once 'phing/BuildException.php';

/**
 * Converts path and classpath information to a specific target OS
 * format. The resulting formatted path is placed into the specified property.
 *
 * @author   Siad Ardroumli <siad.ardroumli@gmail.com>
 *
 * @package  phing.tasks.system
 */
class PathConvert extends Task
{
    // Members
    /**
     * Path to be converted
     */
    private $path = null;
    /**
     * Reference to path/fileset to convert
     * @var Reference $refid
     */
    private $refid = null;
    /**
     * The target OS type
     */
    private $targetOS = null;
    /**
     * Set when targetOS is set to windows
     */
    private $targetWindows = false;
    /**
     * Set if we're running on windows
     */
    public $onWindows = false;
    /**
     * Set if we should create a new property even if the result is empty
     */
    private $setonempty = true;
    /**
     * The property to receive the conversion
     */
    private $property = null;
    /**
     * Path prefix map
     * @var MapEntry[]
     */
    private $prefixMap = array();
    /**
     * User override on path sep char
     */
    private $pathSep = null;
    /**
     * User override on directory sep char
     */
    private $dirSep = null;

    public $from = null;
    public $to = null;

    /**
     * constructor
     */
    public function __construct()
    {
        $this->onWindows = strncasecmp(PHP_OS, 'WIN', 3) === 0;
    }

    /** Create a nested PATH element */
    public function createPath()
    {

        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }

        if ($this->path === null) {
            $this->path = new Path($this->getProject());
        }
        return $this->path->createPath();
    }


    /**
     * Create a nested MAP element
     * @return MapEntry a Map to configure
     */
    public function createMap()
    {
        $entry = new MapEntry($this);

        $this->prefixMap[] = $entry;

        return $entry;
    }


    /**
     * Set targetos to a platform to one of
     * "windows", "unix", "netware", or "os/2"; required unless
     * unless pathsep and/or dirsep are specified.
     */
    public function setTargetos($target)
    {
        $this->targetOS = $target;
        $this->targetWindows = $this->targetOS !== 'unix';
    }

    /**
     * Set setonempty
     *
     * If false, don't set the new property if the result is the empty string.
     * @param bool $setonempty true or false
     */
    public function setSetonempty($setonempty)
    {
        $this->setonempty = $setonempty;
    }

    /**
     * The property into which the converted path will be placed.
     */
    public function setProperty($p)
    {
        $this->property = $p;
    }

    /**
     * Adds a reference to a Path, FileSet, DirSet, or FileList defined
     * elsewhere.
     *
     * @param Reference $r
     *
     * @throws BuildException
     */
    public function setRefid(Reference $r)
    {
        if ($this->path !== null) {
            throw $this->noChildrenAllowed();
        }

        $this->refid = $r;
    }


    /**
     * Set the default path separator string;
     * defaults to current JVM
     *
     * @param string $sep path separator string
     */
    public function setPathSep($sep)
    {
        $this->pathSep = $sep;
    }


    /**
     * Set the default directory separator string
     *
     * @param string $sep directory separator string
     */
    public function setDirSep($sep)
    {
        $this->dirSep = $sep;
    }


    /**
     * Has the refid attribute of this element been set?
     * @return true if refid is valid
     */
    public function isReference()
    {
        return $this->refid !== null;
    }


    /** Do the execution.
     * @throws BuildException if something is invalid
     */
    public function main()
    {
        $savedPath = $this->path;
        $savedPathSep = $this->pathSep;// may be altered in validateSetup
        $savedDirSep = $this->dirSep;// may be altered in validateSetup

        // If we are a reference, create a Path from the reference
        if ($this->isReference()) {
            $this->path = new Path($this->getProject());
            $this->path = $this->path->createPath();

            $obj = $this->refid->getReferencedObject($this->getProject());

            if ($obj instanceof Path) {
                $this->path->setRefid($this->refid);
            } elseif ($obj instanceof FileSet) {
                $fs = $obj;

                $this->path->addFileset($fs);
            } elseif ($obj instanceof DirSet) {
                $ds = $obj;

                $this->path->addDirset($ds);
            } else {
                throw new BuildException("'refid' does not refer to a "
                    . "path, fileset, dirset, or "
                    . "filelist.");
            }
        }

        $this->validateSetup();// validate our setup

        // Currently, we deal with only two path formats: Unix and Windows
        // And Unix is everything that is not Windows
        // (with the exception for NetWare and OS/2 below)

        // for NetWare and OS/2, piggy-back on Windows, since here and
        // in the apply code, the same assumptions can be made as with
        // windows - that \\ is an OK separator, and do comparisons
        // case-insensitive.
        $fromDirSep = $this->onWindows ? "\\" : "/";

        $rslt = '';

        // Get the list of path components in canonical form
        $elems = $this->path->listPaths();

        foreach ($elems as $key => $elem) {
            if (is_string($elem)) {
                $elem = new Path($this->project, $elem);
            }
            $elem = $this->mapElement($elem);// Apply the path prefix map

            // Now convert the path and file separator characters from the
            // current os to the target os.

            if ($key !== 0) {
                $rslt .= $this->pathSep;
            }

            $rslt .= str_replace($fromDirSep, $this->dirSep, $elem);
        }

        // Place the result into the specified property,
        // unless setonempty == false
        $value = $rslt;
        if ($this->setonempty) {
            $this->log("Set property " . $this->property . " = " . $value,
                Project::MSG_VERBOSE);
            $this->getProject()->setNewProperty($this->property, $value);
        } else {
            if ($rslt !== '') {
                $this->log("Set property " . $this->property . " = " . $value,
                    Project::MSG_VERBOSE);
                $this->getProject()->setNewProperty($this->property, $value);
            }
        }

        $this->path = $savedPath;
        $this->dirSep = $savedDirSep;
        $this->pathSep = $savedPathSep;
    }

    /**
     * Apply the configured map to a path element. The map is used to convert
     * between Windows drive letters and Unix paths. If no map is configured,
     * then the input string is returned unchanged.
     *
     * @param string $elem The path element to apply the map to
     * @return String Updated element
     */
    private function mapElement(Path $elem)
    {
        $size = count($this->prefixMap);

        if ($size !== 0) {

            // Iterate over the map entries and apply each one.
            // Stop when one of the entries actually changes the element.

            foreach ($this->prefixMap as $entry) {
                $newElem = $entry->apply((string) $elem);

                // Note I'm using "!=" to see if we got a new object back from
                // the apply method.

                if ($newElem !== (string) $elem) {
                    $elem = $newElem;
                    break;// We applied one, so we're done
                }
            }
        }

        return $elem;
    }

    /**
     * Validate that all our parameters have been properly initialized.
     *
     * @throws BuildException if something is not setup properly
     */
    private function validateSetup()
    {

        if ($this->path === null) {
            throw new BuildException("You must specify a path to convert");
        }

        if ($this->property === null) {
            throw new BuildException("You must specify a property");
        }

        // Must either have a target OS or both a dirSep and pathSep

        if ($this->targetOS == null && $this->pathSep == null && $this->dirSep == null) {
            throw new BuildException("You must specify at least one of "
                . "targetOS, dirSep, or pathSep");
        }

        // Determine the separator strings.  The dirsep and pathsep attributes
        // override the targetOS settings.
        $dsep = PhingFile::$separator;
        $psep = PhingFile::$pathSeparator;

        if ($this->targetOS !== null) {
            $psep = $this->targetWindows ? ";" : ":";
            $dsep = $this->targetWindows ? "\\" : "/";
        }

        if ($this->pathSep !== null) {// override with pathsep=
            $psep = $this->pathSep;
        }

        if ($this->dirSep !== null) {// override with dirsep=
            $dsep = $this->dirSep;
        }

        $this->pathSep = $psep;
        $this->dirSep = $dsep;
    }


    /**
     * Creates an exception that indicates that this XML element must not have
     * child elements if the refid attribute is set.
     */
    private function noChildrenAllowed()
    {
        return new BuildException("You must not specify nested <path> "
            . "elements when using the refid attribute.");
    }

}

/**
 * Helper class, holds the nested &lt;map&gt; values. Elements will look like
 * this: &lt;map from=&quot;d:&quot; to=&quot;/foo&quot;/&gt;
 *
 * When running on windows, the prefix comparison will be case
 * insensitive.
 */
class MapEntry
{
    /** @var PathConvert $outer */
    private $outer;

    public function __construct(PathConvert $outer)
    {
        $this->outer = $outer;
    }

    /**
     * the prefix string to search for; required.
     * Note that this value is case-insensitive when the build is
     * running on a Windows platform and case-sensitive when running on
     * a Unix platform.
     */
    public function setFrom($from)
    {

        $this->outer->from = $from;
    }

    public function setTo($to)
    {
        $this->outer->to = $to;
    }

    /**
     * Apply this map entry to a given path element
     *
     * @param string $elem Path element to process
     * @return string Updated path element after mapping
     *
     * @throws BuildException
     */
    public function apply($elem)
    {
        if ($this->outer->from === null || $this->outer->to === null) {
            throw new BuildException("Both 'from' and 'to' must be set "
                . "in a map entry");
        }

        // If we're on windows, then do the comparison ignoring case
        $cmpElem = $this->outer->onWindows ? strtolower($elem) : $elem;
        $cmpFrom = $this->outer->onWindows ? strtolower(str_replace('/', '\\', $this->outer->from)) : $this->outer->from;

        // If the element starts with the configured prefix, then
        // convert the prefix to the configured 'to' value.

        if (StringHelper::startsWith($cmpFrom, $cmpElem)) {
            $len = strlen($this->outer->from);

            if ($len >= strlen($elem)) {
                $elem = $this->outer->to;
            } else {
                $elem = $this->outer->to . StringHelper::substring($elem, $len);
            }
        }

        return $elem;
    }
}
<?php
/*
 *  $Id: 05920a66ee560b0a27c61fe27a8703df9424ec16 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Call another target in the same project.
 *
 * <samp>
 *    <target name="foo">
 *      <phingcall target="bar">
 *        <property name="property1" value="aaaaa" />
 *        <property name="foo" value="baz" />
 *       </phingcall>
 *    </target>
 *
 *    <target name="bar" depends="init">
 *      <echo message="prop is ${property1} ${foo}" />
 *    </target>
 * </samp>
 *
 * This only works as expected if neither property1 nor foo are defined in the project itself.
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @copyright 2001,2002 THYRELL. All rights reserved
 * @version   $Id: 05920a66ee560b0a27c61fe27a8703df9424ec16 $
 * @package   phing.tasks.system
 */
class PhingCallTask extends Task
{

    /**
     * The called Phing task.
     *
     * @var PhingTask
     */
    private $callee;

    /**
     * The target to call.
     *
     * @var string
     */
    private $subTarget;

    /**
     * Whether to inherit all properties from current project.
     *
     * @var boolean
     */
    private $inheritAll = true;

    /**
     * Whether to inherit refs from current project.
     *
     * @var boolean
     */
    private $inheritRefs = false;

    /**
     *  If true, pass all properties to the new Phing project.
     *  Defaults to true. Future use.
     * @param boolean new value
     */
    public function setInheritAll($inherit)
    {
        $this->inheritAll = (boolean) $inherit;
    }

    /**
     *  If true, pass all references to the new Phing project.
     *  Defaults to false. Future use.
     *
     * @param boolean new value
     */
    public function setInheritRefs($inheritRefs)
    {
        $this->inheritRefs = (boolean) $inheritRefs;
    }

    /**
     * Alias for createProperty
     * @see createProperty()
     */
    public function createParam()
    {
        if ($this->callee === null) {
            $this->init();
        }

        return $this->callee->createProperty();
    }

    /**
     * Property to pass to the invoked target.
     */
    public function createProperty()
    {
        if ($this->callee === null) {
            $this->init();
        }

        return $this->callee->createProperty();
    }

    /**
     * Target to execute, required.
     * @param $target
     */
    public function setTarget($target)
    {
        $this->subTarget = (string) $target;
    }

    /**
     *  init this task by creating new instance of the phing task and
     *  configuring it's by calling its own init method.
     */
    public function init()
    {
        $this->callee = $this->project->createTask("phing");
        $this->callee->setOwningTarget($this->getOwningTarget());
        $this->callee->setTaskName($this->getTaskName());
        $this->callee->setHaltOnFailure(true);
        $this->callee->setLocation($this->getLocation());
        $this->callee->init();
    }

    /**
     *  hand off the work to the phing task of ours, after setting it up
     * @throws BuildException on validation failure or if the target didn't
     *                        execute
     */
    public function main()
    {
        if ($this->getOwningTarget()->getName() === "") {
            $this->log("Cowardly refusing to call target '{$this->subTarget}' from the root", Project::MSG_WARN);
            return;
        }

        $this->log("Running PhingCallTask for target '" . $this->subTarget . "'", Project::MSG_DEBUG);
        if ($this->callee === null) {
            $this->init();
        }

        if ($this->subTarget === null) {
            throw new BuildException("Attribute target is required.", $this->getLocation());
        }

        $this->callee->setPhingfile($this->project->getProperty("phing.file"));
        $this->callee->setTarget($this->subTarget);
        $this->callee->setInheritAll($this->inheritAll);
        $this->callee->setInheritRefs($this->inheritRefs);
        $this->callee->main();
    }

}
<?php

/*
 *  $Id: 43be7ce86f93e18cd2030bb4c764b3e385feebe6 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/Task.php';
include_once 'phing/util/FileUtils.php';
include_once 'phing/types/Reference.php';
include_once 'phing/tasks/system/PropertyTask.php';

/**
 * Task that invokes phing on another build file.
 *
 * Use this task, for example, if you have nested buildfiles in your project. Unlike
 * AntTask, PhingTask can even support filesets:
 *
 * <pre>
 *   <phing>
 *    <fileset dir="${srcdir}">
 *      <include name="** /build.xml" /> <!-- space added after ** is there because of PHP comment syntax -->
 *      <exclude name="build.xml" />
 *    </fileset>
 *   </phing>
 * </pre>
 *
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: 43be7ce86f93e18cd2030bb4c764b3e385feebe6 $
 * @package   phing.tasks.system
 */
class PhingTask extends Task
{

    /** the basedir where is executed the build file */
    private $dir;

    /** build.xml (can be absolute) in this case dir will be ignored */
    private $phingFile;

    /** the target to call if any */
    protected $newTarget;

    /** should we inherit properties from the parent ? */
    private $inheritAll = true;

    /** should we inherit references from the parent ? */
    private $inheritRefs = false;

    /** the properties to pass to the new project */
    private $properties = array();

    /** the references to pass to the new project */
    private $references = array();

    /** The filesets that contain the files PhingTask is to be run on. */
    private $filesets = array();

    /** the temporary project created to run the build file */
    private $newProject;

    /** Fail the build process when the called build fails? */
    private $haltOnFailure = false;

    /**
     *  If true, abort the build process if there is a problem with or in the target build file.
     *  Defaults to false.
     *
     * @param boolean new value
     */
    public function setHaltOnFailure($hof)
    {
        $this->haltOnFailure = (boolean) $hof;
    }

    /**
     * Creates a Project instance for the project to call.
     * @return void
     */
    public function init()
    {
        $this->newProject = new Project();
        $tdf = $this->project->getTaskDefinitions();
        $this->newProject->addTaskDefinition("property", $tdf["property"]);
    }

    /**
     * Called in execute or createProperty if newProject is null.
     *
     * <p>This can happen if the same instance of this task is run
     * twice as newProject is set to null at the end of execute (to
     * save memory and help the GC).</p>
     *
     * <p>Sets all properties that have been defined as nested
     * property elements.</p>
     */
    private function reinit()
    {
        $this->init();
        $count = count($this->properties);
        for ($i = 0; $i < $count; $i++) {
            $p = $this->properties[$i];
            $newP = $this->newProject->createTask("property");
            $newP->setName($p->getName());
            if ($p->getValue() !== null) {
                $newP->setValue($p->getValue());
            }
            if ($p->getFile() !== null) {
                $newP->setFile($p->getFile());
            }
            if ($p->getPrefix() !== null) {
                $newP->setPrefix($p->getPrefix());
            }
            if ($p->getRefid() !== null) {
                $newP->setRefid($p->getRefid());
            }
            if ($p->getEnvironment() !== null) {
                $newP->setEnvironment($p->getEnvironment());
            }
            if ($p->getUserProperty() !== null) {
                $newP->setUserProperty($p->getUserProperty());
            }
            if ($p->getOverride() !== null) {
                $newP->setOverride($p->getOverride());
            }
            $this->properties[$i] = $newP;
        }
    }

    /**
     * Main entry point for the task.
     *
     * @return void
     */
    public function main()
    {

        // Call Phing on the file set with the attribute "phingfile"
        if ($this->phingFile !== null or $this->dir !== null) {
            $this->processFile();
        }

        // if no filesets are given stop here; else process filesets
        if (!empty($this->filesets)) {
            // preserve old settings
            $savedDir = $this->dir;
            $savedPhingFile = $this->phingFile;
            $savedTarget = $this->newTarget;

            // set no specific target for files in filesets
            // [HL] I'm commenting this out; I don't know why this should not be supported!
            // $this->newTarget = null;

            foreach ($this->filesets as $fs) {

                $ds = $fs->getDirectoryScanner($this->project);

                $fromDir = $fs->getDir($this->project);
                $srcFiles = $ds->getIncludedFiles();

                foreach ($srcFiles as $fname) {
                    $f = new PhingFile($ds->getbasedir(), $fname);
                    $f = $f->getAbsoluteFile();
                    $this->phingFile = $f->getAbsolutePath();
                    $this->dir = $f->getParentFile();
                    $this->processFile(); // run Phing!
                }
            }

            // side effect free programming ;-)
            $this->dir = $savedDir;
            $this->phingFile = $savedPhingFile;
            $this->newTarget = $savedTarget;

            // [HL] change back to correct dir
            if ($this->dir !== null) {
                chdir($this->dir->getAbsolutePath());
            }
        }

        // Remove any dangling references to help the GC
        foreach ($this->properties as $property) {
            $property->setFallback(null);
        }
    }

    /**
     * Execute phing file.
     *
     * @throws BuildException
     * @return void
     */
    private function processFile()
    {

        $buildFailed = false;
        $savedDir = $this->dir;
        $savedPhingFile = $this->phingFile;
        $savedTarget = $this->newTarget;

        $savedBasedirAbsPath = null; // this is used to save the basedir *if* we change it

        try {

            if ($this->newProject === null) {
                $this->reinit();
            }

            $this->initializeProject();

            if ($this->dir !== null) {

                $dirAbsPath = $this->dir->getAbsolutePath();

                // BE CAREFUL! -- when the basedir is changed for a project,
                // all calls to getAbsolutePath() on a relative-path dir will
                // be made relative to the project's basedir!  This means
                // that subsequent calls to $this->dir->getAbsolutePath() will be WRONG!

                // We need to save the current project's basedir first.
                $savedBasedirAbsPath = $this->getProject()->getBasedir()->getAbsolutePath();

                $this->newProject->setBasedir($this->dir);

                // Now we must reset $this->dir so that it continues to resolve to the same
                // path.
                $this->dir = new PhingFile($dirAbsPath);

                if ($savedDir !== null) { // has been set explicitly
                    $this->newProject->setInheritedProperty("project.basedir", $this->dir->getAbsolutePath());
                }

            } else {

                // Since we're not changing the basedir here (for file resolution),
                // we don't need to worry about any side-effects in this scanrio.
                $this->dir = $this->getProject()->getBasedir();
            }

            $this->overrideProperties();
            if ($this->phingFile === null) {
                $this->phingFile = "build.xml";
            }

            $fu = new FileUtils();
            $file = $fu->resolveFile($this->dir, $this->phingFile);
            $this->phingFile = $file->getAbsolutePath();

            $this->log("Calling Buildfile '" . $this->phingFile . "' with target '" . $this->newTarget . "'", Project::MSG_VERBOSE);

            $this->newProject->setUserProperty("phing.file", $this->phingFile);

            ProjectConfigurator::configureProject($this->newProject, new PhingFile($this->phingFile));

            if ($this->newTarget === null) {
                $this->newTarget = $this->newProject->getDefaultTarget();
            }

            // Are we trying to call the target in which we are defined?
            if ($this->newProject->getBaseDir() == $this->project->getBaseDir() &&
                $this->newProject->getProperty("phing.file") == $this->project->getProperty("phing.file") &&
                $this->getOwningTarget() !== null &&
                $this->newTarget == $this->getOwningTarget()->getName()
            ) {

                throw new BuildException("phing task calling its own parent target");
            }

            $this->addReferences();
            $this->newProject->executeTarget($this->newTarget);

        } catch (Exception $e) {
            $buildFailed = true;
            $this->log($e->getMessage(), Project::MSG_ERR);
            if (Phing::getMsgOutputLevel() <= Project::MSG_DEBUG) {
                $lines = explode("\n", $e->getTraceAsString());
                foreach ($lines as $line) {
                    $this->log($line, Project::MSG_DEBUG);
                }
            }
            // important!!! continue on to perform cleanup tasks.
        }

        // reset environment values to prevent side-effects.

        $this->newProject = null;
        $pkeys = array_keys($this->properties);
        foreach ($pkeys as $k) {
            $this->properties[$k]->setProject(null);
        }

        $this->dir = $savedDir;
        $this->phingFile = $savedPhingFile;
        $this->newTarget = $savedTarget;

        // If the basedir for any project was changed, we need to set that back here.
        if ($savedBasedirAbsPath !== null) {
            chdir($savedBasedirAbsPath);
        }

        if ($this->haltOnFailure && $buildFailed) {
            throw new BuildException("Execution of the target buildfile failed. Aborting.");
        }
    }

    /**
     * Configure the Project, i.e. make intance, attach build listeners
     * (copy from father project), add Task and Datatype definitions,
     * copy properties and references from old project if these options
     * are set via the attributes of the XML tag.
     *
     * Developer note:
     * This function replaces the old methods "init", "_reinit" and
     * "_initializeProject".
     *
     */
    private function initializeProject()
    {

        $this->newProject->setInputHandler($this->project->getInputHandler());

        foreach ($this->project->getBuildListeners() as $listener) {
            $this->newProject->addBuildListener($listener);
        }

        /* Copy things from old project. Datatypes and Tasks are always
         * copied, properties and references only if specified so/not
         * specified otherwise in the XML definition.
         */
        // Add Datatype definitions
        foreach ($this->project->getDataTypeDefinitions() as $typeName => $typeClass) {
            $this->newProject->addDataTypeDefinition($typeName, $typeClass);
        }

        // Add Task definitions
        foreach ($this->project->getTaskDefinitions() as $taskName => $taskClass) {
            if ($taskClass == "propertytask") {
                // we have already added this taskdef in init()
                continue;
            }
            $this->newProject->addTaskDefinition($taskName, $taskClass);
        }

        // set user-defined properties
        $this->project->copyUserProperties($this->newProject);

        if (!$this->inheritAll) {
            // set System built-in properties separately,
            // b/c we won't inherit them.
            $this->newProject->setSystemProperties();

        } else {
            // set all properties from calling project
            $properties = $this->project->getProperties();
            foreach ($properties as $name => $value) {
                if ($name == "basedir" || $name == "phing.file" || $name == "phing.version") {
                    // basedir and phing.file get special treatment in main()
                    continue;
                }
                // don't re-set user properties, avoid the warning message
                if ($this->newProject->getProperty($name) === null) {
                    // no user property
                    $this->newProject->setNewProperty($name, $value);
                }
            }

        }

    }

    /**
     * Override the properties in the new project with the one
     * explicitly defined as nested elements here.
     * @return void
     * @throws BuildException
     */
    private function overrideProperties()
    {
        foreach (array_keys($this->properties) as $i) {
            $p = $this->properties[$i];
            $p->setProject($this->newProject);
            $p->main();
        }
        $this->project->copyInheritedProperties($this->newProject);
    }

    /**
     * Add the references explicitly defined as nested elements to the
     * new project.  Also copy over all references that don't override
     * existing references in the new project if inheritrefs has been
     * requested.
     *
     * @return void
     * @throws BuildException
     */
    private function addReferences()
    {

        // parent project references
        $projReferences = $this->project->getReferences();

        $newReferences = $this->newProject->getReferences();

        $subprojRefKeys = array();

        if (count($this->references) > 0) {
            for ($i = 0, $count = count($this->references); $i < $count; $i++) {
                $ref = $this->references[$i];
                $refid = $ref->getRefId();

                if ($refid === null) {
                    throw new BuildException("the refid attribute is required"
                        . " for reference elements");
                }
                if (!isset($projReferences[$refid])) {
                    $this->log(
                        "Parent project doesn't contain any reference '"
                        . $refid . "'",
                        Project::MSG_WARN
                    );
                    continue;
                }

                $subprojRefKeys[] = $refid;
                //thisReferences.remove(refid);
                $toRefid = $ref->getToRefid();
                if ($toRefid === null) {
                    $toRefid = $refid;
                }
                $this->copyReference($refid, $toRefid);
            }
        }

        // Now add all references that are not defined in the
        // subproject, if inheritRefs is true
        if ($this->inheritRefs) {

            // get the keys that are were not used by the subproject
            $unusedRefKeys = array_diff(array_keys($projReferences), $subprojRefKeys);

            foreach ($unusedRefKeys as $key) {
                if (isset($newReferences[$key])) {
                    continue;
                }
                $this->copyReference($key, $key);
            }
        }
    }

    /**
     * Try to clone and reconfigure the object referenced by oldkey in
     * the parent project and add it to the new project with the key
     * newkey.
     *
     * <p>If we cannot clone it, copy the referenced object itself and
     * keep our fingers crossed.</p>
     *
     * @param  string $oldKey
     * @param  string $newKey
     * @throws BuildException
     * @return void
     */
    private function copyReference($oldKey, $newKey)
    {
        $orig = $this->project->getReference($oldKey);
        if ($orig === null) {
            $this->log(
                "No object referenced by " . $oldKey . ". Can't copy to "
                . $newKey,
                Project::MSG_WARN
            );

            return;
        }

        $copy = clone $orig;

        if ($copy instanceof ProjectComponent) {
            $copy->setProject($this->newProject);
        } elseif (in_array('setProject', get_class_methods(get_class($copy)))) {
            $copy->setProject($this->newProject);
        } elseif ($copy instanceof Project) {
            // don't copy the old "Project" itself
        } else {
            $msg = "Error setting new project instance for "
                . "reference with id " . $oldKey;
            throw new BuildException($msg);
        }

        $this->newProject->addReference($newKey, $copy);
    }

    /**
     * If true, pass all properties to the new phing project.
     * Defaults to true.
     *
     * @param $value
     */
    public function setInheritAll($value)
    {
        $this->inheritAll = (boolean) $value;
    }

    /**
     * If true, pass all references to the new phing project.
     * Defaults to false.
     *
     * @param $value
     */
    public function setInheritRefs($value)
    {
        $this->inheritRefs = (boolean) $value;
    }

    /**
     * The directory to use as a base directory for the new phing project.
     * Defaults to the current project's basedir, unless inheritall
     * has been set to false, in which case it doesn't have a default
     * value. This will override the basedir setting of the called project.
     *
     * @param $d
     */
    public function setDir($d)
    {
        if (is_string($d)) {
            $this->dir = new PhingFile($d);
        } else {
            $this->dir = $d;
        }
    }

    /**
     * The build file to use.
     * Defaults to "build.xml". This file is expected to be a filename relative
     * to the dir attribute given.
     *
     * @param $s
     */
    public function setPhingfile($s)
    {
        // it is a string and not a file to handle relative/absolute
        // otherwise a relative file will be resolved based on the current
        // basedir.
        $this->phingFile = $s;
    }

    /**
     * Alias function for setPhingfile
     *
     * @param $s
     */
    public function setBuildfile($s)
    {
        $this->setPhingFile($s);
    }

    /**
     * The target of the new Phing project to execute.
     * Defaults to the new project's default target.
     *
     * @param $s
     */
    public function setTarget($s)
    {
        $this->newTarget = $s;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Property to pass to the new project.
     * The property is passed as a 'user property'
     *
     */
    public function createProperty()
    {
        $p = new PropertyTask();
        $p->setFallback($this->newProject);
        $p->setUserProperty(true);
        $this->properties[] = $p;

        return $p;
    }

    /**
     * Reference element identifying a data type to carry
     * over to the new project.
     *
     */
    public function createReference()
    {
        $num = array_push($this->references, new PhingReference());

        return $this->references[$num - 1];
    }

}

/**
 * Helper class that implements the nested <reference>
 * element of <phing> and <phingcall>.
 *
 * @package   phing.tasks.system
 */
class PhingReference extends Reference
{

    private $targetid = null;

    /**
     * Set the id that this reference to be stored under in the
     * new project.
     *
     * @param the $targetid
     * @internal param the $targetid id under which this reference will be passed to
     *        the new project
     */
    public function setToRefid($targetid)
    {
        $this->targetid = $targetid;
    }

    /**
     * Get the id under which this reference will be stored in the new
     * project
     *
     * @return the id of the reference in the new project.
     */
    public function getToRefid()
    {
        return $this->targetid;
    }
}
<?php
/*
 *  $Id: 65544d82e211a20bd0284c48eef42341b3437cfd $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Executes PHP function or evaluates expression and sets return value to a property.
 *
 *    WARNING:
 *        This task can, of course, be abused with devastating effects.  E.g. do not
 *        modify internal Phing classes unless you know what you are doing.
 *
 * @author   Hans Lellelid <hans@xmpl.org>
 * @version  $Id: 65544d82e211a20bd0284c48eef42341b3437cfd $
 * @package  phing.tasks.system
 *
 * @todo Add support for evaluating expressions
 */
class PhpEvalTask extends Task
{

    protected $expression; // Expression to evaluate
    protected $function; // Function to execute
    protected $class; // Class containing function to execute
    protected $returnProperty = null; // name of property to set to return value
    protected $params = array(); // parameters for function calls

    protected $logLevel = Project::MSG_INFO;

    /**
     * Set level of log messages generated (default = info)
     * @param string $level
     */
    public function setLevel($level)
    {
        switch ($level) {
            case "error":
                $this->logLevel = Project::MSG_ERR;
                break;
            case "warning":
                $this->logLevel = Project::MSG_WARN;
                break;
            case "info":
                $this->logLevel = Project::MSG_INFO;
                break;
            case "verbose":
                $this->logLevel = Project::MSG_VERBOSE;
                break;
            case "debug":
                $this->logLevel = Project::MSG_DEBUG;
                break;
        }
    }

    /** Main entry point. */
    public function main()
    {

        if ($this->function === null && $this->expression === null) {
            throw new BuildException("You must specify a function to execute or PHP expression to evalute.", $this->location);
        }

        if ($this->function !== null && $this->expression !== null) {
            throw new BuildException("You can specify function or expression, but not both.", $this->location);
        }

        if ($this->expression !== null && !empty($this->params)) {
            throw new BuildException("You cannot use nested <param> tags when evaluationg a PHP expression.", $this->location);
        }

        if ($this->function !== null) {
            $this->callFunction();
        } elseif ($this->expression !== null) {
            $this->evalExpression();
        }
    }

    /**
     * Calls function and returns results.
     * @return mixed
     */
    protected function callFunction()
    {

        if ($this->class !== null) {
            // import the classname & unqualify it, if necessary
            $this->class = Phing::import($this->class);

            $user_func = array($this->class, $this->function);
            $h_func = $this->class . '::' . $this->function; // human-readable (for log)
        } else {
            $user_func = $this->function;
            $h_func = $user_func; // human-readable (for log)
        }

        // put parameters into simple array
        $params = array();
        foreach ($this->params as $p) {
            $params[] = $p->getValue();
        }

        $this->log("Calling PHP function: " . $h_func . "()", $this->logLevel);
        foreach ($params as $p) {
            $this->log("  param: " . $p, Project::MSG_VERBOSE);
        }

        $return = call_user_func_array($user_func, $params);

        if ($this->returnProperty !== null) {
            $this->project->setProperty($this->returnProperty, $return);
        }
    }

    /**
     * Evaluates expression and returns resulting value.
     * @return mixed
     */
    protected function evalExpression()
    {
        $this->log("Evaluating PHP expression: " . $this->expression, $this->logLevel);
        if (!StringHelper::endsWith(';', trim($this->expression))) {
            $this->expression .= ';';
        }

        if ($this->returnProperty !== null) {
            $retval = null;
            eval('$retval = ' . $this->expression);
            $this->project->setProperty($this->returnProperty, $retval);
        } else {
            eval($this->expression);
        }
    }

    /** Set function to execute
     * @param $f
     */
    public function setFunction($f)
    {
        $this->function = $f;
    }

    /** Set [static] class which contains function to execute
     * @param $c
     */
    public function setClass($c)
    {
        $this->class = $c;
    }

    /** Sets property name to set with return value of function or expression.
     * @param $r
     */
    public function setReturnProperty($r)
    {
        $this->returnProperty = $r;
    }

    /** Set PHP expression to evaluate.
     * @param $expression
     */
    public function addText($expression)
    {
        $this->expression = $expression;
    }

    /** Set PHP expression to evaluate.
     * @param $expression
     */
    public function setExpression($expression)
    {
        $this->expression = $expression;
    }

    /** Add a nested <param> tag. */
    public function createParam()
    {
        $p = new FunctionParam();
        $this->params[] = $p;

        return $p;
    }
}

/**
 * Supports the <param> nested tag for PhpTask.
 *
 * @package  phing.tasks.system
 */
class FunctionParam
{

    private $val;

    /**
     * @param $v
     */
    public function setValue($v)
    {
        $this->val = $v;
    }

    /**
     * @param $v
     */
    public function addText($v)
    {
        $this->val = $v;
    }

    public function getValue()
    {
        return $this->val;
    }
}
<?php
/*
 *  $Id: de25faf5f70bf92026b2d934db52a6a05d8dce5e $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/input/InputRequest.php';

/**
 * Deprecated task that uses console to prompt user for property values.
 *
 * This class is very slightly simpler than the InputTask, but lacks the ability
 * to use a non-console input handler.  You should, therefore, use InputTask.  This
 * class can serve as a reference, but will be removed in the future.
 *
 * @author    Hans Lellelid <hans@xmpl.org> (Phing)
 * @author    Anthony J. Young-Garner <ajyoung@alum.mit.edu> (Ant)
 * @version   $Id: de25faf5f70bf92026b2d934db52a6a05d8dce5e $
 * @package   phing.tasks.system
 * @deprecated - in favor of the more capable InputTask
 */
class PropertyPromptTask extends Task
{

    /**
     * The property name to set with the output.
     * @var string
     */
    private $propertyName; // required

    /**
     * The default value to use if no input is entered.
     * @var string
     */
    private $defaultValue;

    /**
     * The entered value.
     * @var string
     */
    private $proposedValue;

    /**
     * The text to use for the prompt.
     * @var string
     */
    private $promptText;

    /**
     * The character to put after the text.
     * @var string
     */
    private $promptCharacter;

    /**
     *
     */
    private $useExistingValue;

    /**
     * Run the PropertyPrompt task.
     * @throws BuildException
     */
    public function main()
    {

        $this->proposedValue = $this->project->getProperty($this->propertyName);
        $currentValue = $this->defaultValue;

        if ($currentValue == "" && $this->proposedValue !== null) {
            $currentValue = $this->proposedValue;
        }

        if ($this->useExistingValue !== true || $this->proposedValue === null) {

            $this->log(
                "Prompting user for " . $this->propertyName . ". " . $this->getDefaultMessage(),
                Project::MSG_VERBOSE
            );

            $promptText = "\n" . $this->promptText . " [" . $currentValue . "] " . $this->promptCharacter . " ";

            try {
                $request = new InputRequest($promptText);
                $this->project->getInputHandler()->handleInput($request);
                $this->proposedValue = $request->getInput();
            } catch (IOException $e) {
                $this->log("Prompt failed. Using default. (Failure reason: " . $e->getMessage() . ")");
                $this->proposedValue = $this->defaultValue;
            }

            if ($this->proposedValue === "") {
                $this->log("No value specified, using default.", Project::MSG_VERBOSE);
                $this->proposedValue = $this->defaultValue;
            }

            if (isset($this->proposedValue)) {
                $this->project->setProperty($this->propertyName, $this->proposedValue);
            }

        }
    }

    /**
     * Returns a string to be inserted in the log message
     * indicating whether a default response was specified
     * in the build file.
     */
    private function getDefaultMessage()
    {
        if ($this->defaultValue == "") {
            return "No default response specified.";
        } else {
            return "Default response is " . $this->defaultValue . ".";
        }
    }

    /**
     * Returns defaultValue specified
     * in this task for the Property
     * being set.
     * @return string
     */
    public function getDefaultValue()
    {
        return $this->defaultValue;
    }

    /**
     * Returns the terminating character used to
     * punctuate the prompt text.
     * @return string
     */
    public function getPromptCharacter()
    {
        return $this->promptCharacter;
    }

    /**
     * Returns text of the prompt.
     * @return java.lang.String
     */
    public function getPromptText()
    {
        return $this->promptText;
    }

    /**
     * Returns name of the Ant Project Property
     * being set by this task.
     * @return string
     */
    public function getPropertyName()
    {
        return $this->propertyName;
    }

    /**
     * Initializes this task.
     */
    public function init()
    {
        parent::init();
        $this->defaultValue = "";
        $this->promptCharacter = "?";
        $this->useExistingValue = false;
    }

    /**
     * Insert the method's description here.
     * Creation date: (12/10/2001 8:16:16 AM)
     * @return boolean
     */
    public function isUseExistingValue()
    {
        return $this->useExistingValue;
    }

    /**
     * Sets defaultValue for the Property
     * being set by this task.
     * @param string $newDefaultvalue
     */
    public function setDefaultvalue($newDefaultvalue)
    {
        $this->defaultValue = $newDefaultvalue;
    }

    /**
     * Sets the terminating character used to
     * punctuate the prompt text (default is "?").
     * @param string $newPromptcharacter
     */
    public function setPromptCharacter($newPromptcharacter)
    {
        $this->promptCharacter = $newPromptcharacter;
    }

    /**
     * Sets text of the prompt.
     * @param string $newPrompttext
     */
    public function setPromptText($newPrompttext)
    {
        $this->promptText = $newPrompttext;
    }

    /**
     * Specifies the Phing Project Property
     * being set by this task.
     * @param java $newPropertyname
     * @internal param java $newPropertyname .lang.String
     */
    public function setPropertyName($newPropertyname)
    {
        $this->propertyName = $newPropertyname;
    }

    /**
     *
     * @param boolean $newUseExistingValue
     */
    public function setUseExistingValue($newUseExistingValue)
    {
        $this->useExistingValue = $newUseExistingValue;
    }

    /**
     * Sets the prompt text that will be presented to the user.
     * @param  string $prompt
     * @return void
     */
    public function addText($prompt)
    {
        $this->setPromptText($prompt);
    }

}
<?php

/*
 *  $Id: e25b4d62e56e9dad7e7e776ef7214a9557e0f18d $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/Task.php';
include_once 'phing/system/util/Properties.php';
include_once 'phing/system/io/FileParserFactoryInterface.php';
include_once 'phing/system/io/FileParserFactory.php';

/**
 * Task for setting properties in buildfiles.
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: e25b4d62e56e9dad7e7e776ef7214a9557e0f18d $
 * @package   phing.tasks.system
 */
class PropertyTask extends Task
{

    /** name of the property */
    protected $name;

    /** value of the property */
    protected $value;

    protected $reference;
    protected $env; // Environment
    protected $file;
    protected $ref;
    protected $prefix;
    protected $fallback;

    /** Whether to force overwrite of existing property. */
    protected $override = false;

    /** Whether property should be treated as "user" property. */
    protected $userProperty = false;

    /**
     * All filterchain objects assigned to this task
     */
    protected $filterChains = array();

    /** Whether to log messages as INFO or VERBOSE  */
    protected $logOutput = true;

    /**
     * @var FileParserFactoryInterface
     */
    private $fileParserFactory;

    /**
     * @param FileParserFactoryInterface $fileParserFactory
     */
    public function __construct(FileParserFactoryInterface $fileParserFactory = null)
    {
        $this->fileParserFactory = $fileParserFactory != null ? $fileParserFactory : new FileParserFactory();
    }

    /**
     * Sets a the name of current property component
     * @param $name
     */
    public function setName($name)
    {
        $this->name = (string) $name;
    }

    /** Get property component name. */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Sets a the value of current property component.
     * @param    mixed      Value of name, all scalars allowed
     */
    public function setValue($value)
    {
        $this->value = (string) $value;
    }

    /**
     * Sets value of property to CDATA tag contents.
     * @param $value
     * @internal param string $values
     * @since 2.2.0
     */
    public function addText($value)
    {
        $this->setValue($value);
    }

    /** Get the value of current property component. */
    public function getValue()
    {
        return $this->value;
    }

    /** Set a file to use as the source for properties.
     * @param $file
     */
    public function setFile($file)
    {
        if (is_string($file)) {
            $file = new PhingFile($file);
        }
        $this->file = $file;
    }

    /** Get the PhingFile that is being used as property source. */
    public function getFile()
    {
        return $this->file;
    }

    /**
     * @param Reference $ref
     */
    public function setRefid(Reference $ref)
    {
        $this->reference = $ref;
    }

    public function getRefid()
    {
        return $this->reference;
    }

    /**
     * Prefix to apply to properties loaded using <code>file</code>.
     * A "." is appended to the prefix if not specified.
     * @param  string $prefix prefix string
     * @return void
     * @since 2.0
     */
    public function setPrefix($prefix)
    {
        $this->prefix = $prefix;
        if (!StringHelper::endsWith(".", $prefix)) {
            $this->prefix .= ".";
        }
    }

    /**
     * @return string
     * @since 2.0
     */
    public function getPrefix()
    {
        return $this->prefix;
    }

    /**
     * the prefix to use when retrieving environment variables.
     * Thus if you specify environment="myenv"
     * you will be able to access OS-specific
     * environment variables via property names "myenv.PATH" or
     * "myenv.TERM".
     * <p>
     * Note that if you supply a property name with a final
     * "." it will not be doubled. ie environment="myenv." will still
     * allow access of environment variables through "myenv.PATH" and
     * "myenv.TERM". This functionality is currently only implemented
     * on select platforms. Feel free to send patches to increase the number of platforms
     * this functionality is supported on ;).<br>
     * Note also that properties are case sensitive, even if the
     * environment variables on your operating system are not, e.g. it
     * will be ${env.Path} not ${env.PATH} on Windows 2000.
     * @param prefix $env
     * @internal param prefix $env
     */
    public function setEnvironment($env)
    {
        $this->env = (string) $env;
    }

    public function getEnvironment()
    {
        return $this->env;
    }

    /**
     * Set whether this is a user property (ro).
     * This is deprecated in Ant 1.5, but the userProperty attribute
     * of the class is still being set via constructor, so Phing will
     * allow this method to function.
     * @param boolean $v
     */
    public function setUserProperty($v)
    {
        $this->userProperty = (boolean) $v;
    }

    /**
     * @return bool
     */
    public function getUserProperty()
    {
        return $this->userProperty;
    }

    /**
     * @param $v
     */
    public function setOverride($v)
    {
        $this->override = (boolean) $v;
    }

    /**
     * @return bool
     */
    public function getOverride()
    {
        return $this->override;
    }

    /**
     * @return string
     */
    public function toString()
    {
        return (string) $this->value;
    }

    /**
     * @param Project $p
     */
    public function setFallback($p)
    {
        $this->fallback = $p;
    }

    public function getFallback()
    {
        return $this->fallback;
    }

    /**
     * Creates a filterchain
     *
     * @return object The created filterchain object
     */
    public function createFilterChain()
    {
        $num = array_push($this->filterChains, new FilterChain($this->project));

        return $this->filterChains[$num - 1];
    }

    /**
     * @param $logOutput
     */
    public function setLogoutput($logOutput)
    {
        $this->logOutput = (bool) $logOutput;
    }

    /**
     * @return bool
     */
    public function getLogoutput()
    {
        return $this->logOutput;
    }

    /**
     * set the property in the project to the value.
     * if the task was give a file or env attribute
     * here is where it is loaded
     */
    public function main()
    {
        if ($this->name !== null) {
            if ($this->value === null && $this->reference === null) {
                throw new BuildException("You must specify value or refid with the name attribute", $this->getLocation(
                ));
            }
        } else {
            if ($this->file === null && $this->env === null) {
                throw new BuildException(
                    "You must specify file or environment when not using the name attribute",
                    $this->getLocation()
                );
            }
        }

        if ($this->file === null && $this->prefix !== null) {
            throw new BuildException("Prefix is only valid when loading from a file.", $this->getLocation());
        }

        if (($this->name !== null) && ($this->value !== null)) {
            $this->addProperty($this->name, $this->value);
        }

        if ($this->file !== null) {
            $this->loadFile($this->file);
        }

        if ($this->env !== null) {
            $this->loadEnvironment($this->env);
        }

        if (($this->name !== null) && ($this->reference !== null)) {
            // get the refereced property
            try {
                $referencedObject = $this->reference->getReferencedObject($this->project);

                if ($referencedObject instanceof Exception) {
                    $reference = $referencedObject->getMessage();
                } elseif (method_exists($referencedObject, 'toString')) {
                    $reference = $referencedObject->toString();
                } else {
                    $reference = (string) $referencedObject;
                }

                $this->addProperty($this->name, $reference);
            } catch (BuildException $be) {
                if ($this->fallback !== null) {
                    $referencedObject = $this->reference->getReferencedObject($this->fallback);

                    if ($referencedObject instanceof Exception) {
                        $reference = $referencedObject->getMessage();
                    } elseif (method_exists($referencedObject, 'toString')) {
                        $reference = $referencedObject->toString();
                    } else {
                        $reference = (string) $referencedObject;
                    }
                    $this->addProperty($this->name, $reference);
                } else {
                    throw $be;
                }
            }
        }
    }

    /**
     * load the environment values
     * @param string $prefix prefix to place before them
     */
    protected function loadEnvironment($prefix)
    {

        $props = new Properties();
        if (substr($prefix, strlen($prefix) - 1) == '.') {
            $prefix .= ".";
        }
        $this->log("Loading Environment $prefix", Project::MSG_VERBOSE);
        foreach ($_ENV as $key => $value) {
            $props->setProperty($prefix . '.' . $key, $value);
        }
        $this->addProperties($props);
    }

    /**
     * iterate through a set of properties,
     * resolve them then assign them
     * @param $props
     * @throws BuildException
     */
    protected function addProperties($props)
    {
        $this->resolveAllProperties($props);
        foreach ($props->keys() as $name) {
            $value = $props->getProperty($name);
            $v = $this->project->replaceProperties($value);
            if ($this->prefix !== null) {
                $name = $this->prefix . $name;
            }
            $this->addProperty($name, $v);
        }
    }

    /**
     * add a name value pair to the project property set
     * @param string $name  name of property
     * @param string $value value to set
     */
    protected function addProperty($name, $value)
    {
        if (count($this->filterChains) > 0) {
            $in = FileUtils::getChainedReader(new StringReader($value), $this->filterChains, $this->project);
            $value = $in->read();
        }

        if ($this->userProperty) {
            if ($this->project->getUserProperty($name) === null || $this->override) {
                $this->project->setInheritedProperty($name, $value);
            } else {
                $this->log("Override ignored for " . $name, Project::MSG_VERBOSE);
            }
        } else {
            if ($this->override) {
                $this->project->setProperty($name, $value);
            } else {
                $this->project->setNewProperty($name, $value);
            }
        }
    }

    /**
     * load properties from a file.
     * @param PhingFile $file
     * @throws BuildException
     */
    protected function loadFile(PhingFile $file)
    {
        $fileParser = $this->fileParserFactory->createParser($file->getFileExtension());
        $props = new Properties(null, $fileParser);
        $this->log("Loading " . $file->getAbsolutePath(), $this->logOutput ? Project::MSG_INFO : Project::MSG_VERBOSE);
        try { // try to load file
            if ($file->exists()) {
                $props->load($file);
                $this->addProperties($props);
            } else {
                $this->log(
                    "Unable to find property file: " . $file->getAbsolutePath() . "... skipped",
                    Project::MSG_WARN
                );
            }
        } catch (IOException $ioe) {
            throw new BuildException("Could not load properties from file.", $ioe);
        }
    }

    /**
     * Given a Properties object, this method goes through and resolves
     * any references to properties within the object.
     *
     * @param  Properties $props The collection of Properties that need to be resolved.
     * @throws BuildException
     * @return void
     */
    protected function resolveAllProperties(Properties $props)
    {

        foreach ($props->keys() as $name) {
            // There may be a nice regex/callback way to handle this
            // replacement, but at the moment it is pretty complex, and
            // would probably be a lot uglier to work into a preg_replace_callback()
            // system.  The biggest problem is the fact that a resolution may require
            // multiple passes.

            $value = $props->getProperty($name);
            $resolved = false;
            $resolveStack = array();

            while (!$resolved) {

                $fragments = array();
                $propertyRefs = array();

                // [HL] this was ::parsePropertyString($this->value ...) ... this seems wrong
                self::parsePropertyString($value, $fragments, $propertyRefs);

                $resolved = true;
                if (count($propertyRefs) == 0) {
                    continue;
                }

                $sb = "";

                $j = $propertyRefs;

                foreach ($fragments as $fragment) {
                    if ($fragment !== null) {
                        $sb .= $fragment;
                        continue;
                    }

                    $propertyName = array_shift($j);
                    if (in_array($propertyName, $resolveStack)) {
                        // Should we maybe just log this as an error & move on?
                        // $this->log("Property ".$name." was circularly defined.", Project::MSG_ERR);
                        throw new BuildException("Property " . $propertyName . " was circularly defined.");
                    }

                    $fragment = $this->getProject()->getProperty($propertyName);
                    if ($fragment !== null) {
                        $sb .= $fragment;
                        continue;
                    }

                    if ($props->containsKey($propertyName)) {
                        $fragment = $props->getProperty($propertyName);
                        if (strpos($fragment, '${') !== false) {
                            $resolveStack[] = $propertyName;
                            $resolved = false; // parse again (could have been replaced w/ another var)
                        }
                    } else {
                        $fragment = "\${" . $propertyName . "}";
                    }

                    $sb .= $fragment;
                }

                $this->log("Resolved Property \"$value\" to \"$sb\"", Project::MSG_DEBUG);
                $value = $sb;
                $props->setProperty($name, $value);
            } // while (!$resolved)

        } // while (count($keys)
    }

    /**
     * This method will parse a string containing ${value} style
     * property values into two lists. The first list is a collection
     * of text fragments, while the other is a set of string property names
     * null entries in the first list indicate a property reference from the
     * second list.
     *
     * This is slower than regex, but useful for this class, which has to handle
     * multiple parsing passes for properties.
     *
     * @param string $value The string to be scanned for property references
     * @param array &$fragments The found fragments
     * @param array &$propertyRefs The found refs
     * @throws BuildException
     */
    protected function parsePropertyString($value, &$fragments, &$propertyRefs)
    {

        $prev = 0;
        $pos = 0;

        while (($pos = strpos($value, '$', $prev)) !== false) {

            if ($pos > $prev) {
                array_push($fragments, StringHelper::substring($value, $prev, $pos - 1));
            }
            if ($pos === (strlen($value) - 1)) {
                array_push($fragments, '$');
                $prev = $pos + 1;
            } elseif ($value{$pos + 1} !== '{') {

                // the string positions were changed to value-1 to correct
                // a fatal error coming from function substring()
                array_push($fragments, StringHelper::substring($value, $pos, $pos + 1));
                $prev = $pos + 2;
            } else {
                $endName = strpos($value, '}', $pos);
                if ($endName === false) {
                    throw new BuildException("Syntax error in property: $value");
                }
                $propertyName = StringHelper::substring($value, $pos + 2, $endName - 1);
                array_push($fragments, null);
                array_push($propertyRefs, $propertyName);
                $prev = $endName + 1;
            }
        }

        if ($prev < strlen($value)) {
            array_push($fragments, StringHelper::substring($value, $prev));
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/BuildEvent.php';
include_once 'phing/BuildLogger.php';
include_once 'phing/Phing.php';
include_once 'phing/Project.php';
include_once 'phing/SubBuildListener.php';
include_once 'phing/util/StringHelper.php';
include_once 'phing/system/io/FileOutputStream.php';
include_once 'phing/system/io/IOException.php';
include_once 'phing/BuildException.php';

/**
 * This is a class that represents a recorder. This is the listener to the
 * build process.
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing.tasks.system
 */
class RecorderEntry implements BuildLogger, SubBuildListener
{
    /**
     * The name of the file associated with this recorder entry.
     * @var string $filename
     */
    private $filename = null;

    /**
     * The state of the recorder (recorder on or off).
     * @var bool $record
     */
    private $record = true;

    /** The current verbosity level to record at.  */
    private $loglevel;

    /**
     * The output OutputStream to record to.
     * @var OutputStream $out
     */
    private $out = null;

    /** The start time of the last know target.  */
    private $targetStartTime;

    /** Strip task banners if true.  */
    private $emacsMode = false;

    /**
     * project instance the recorder is associated with
     * @var Project $project
     */
    private $project;

    /**
     * @param string $name The name of this recorder (used as the filename).
     */
    public function __construct($name)
    {
        $this->targetStartTime = Phing::currentTimeMillis();
        $this->filename = $name;
        $this->loglevel = Project::MSG_INFO;
    }

    /**
     * @return string the name of the file the output is sent to.
     */
    public function getFilename()
    {
        return $this->filename;
    }

    /**
     * Turns off or on this recorder.
     *
     * @param bool|null state true for on, false for off, null for no change.
     */
    public function setRecordState($state)
    {
        if ($state != null) {
            $this->flush();
            $this->record = StringHelper::booleanValue($state);
        }
    }

    /** {@inheritDoc}. */
    public function buildStarted(BuildEvent $event)
    {
        $this->log("> BUILD STARTED", Project::MSG_DEBUG);
    }

    /** {@inheritDoc}. */
    public function buildFinished(BuildEvent $event)
    {
        $this->log("< BUILD FINISHED", Project::MSG_DEBUG);

        if ($this->record && $this->out != null) {
            $error = $event->getException();

            if ($error == null) {
                $this->out->write(Phing::getProperty('line.separator') . "BUILD SUCCESSFUL" . PHP_EOL);
            } else {
                $this->out->write(Phing::getProperty('line.separator') . "BUILD FAILED"
                    . Phing::getProperty('line.separator') . PHP_EOL);
                $this->out->write($error->getTraceAsString());
            }
        }
        $this->cleanup();
    }

    /**
     * Cleans up any resources held by this recorder entry at the end
     * of a subbuild if it has been created for the subbuild's project
     * instance.
     *
     * @param BuildEvent $event the buildFinished event
     */
    public function subBuildFinished(BuildEvent $event)
    {
        if ($event->getProject() == $this->project) {
            $this->cleanup();
        }
    }

    /**
     * Empty implementation to satisfy the BuildListener interface.
     *
     * @param BuildEvent $event the buildStarted event
     */
    public function subBuildStarted(BuildEvent $event)
    {
    }

    /** {@inheritDoc}. */
    public function targetStarted(BuildEvent $event)
    {
        $this->log(">> TARGET STARTED -- " . $event->getTarget()->getName(), Project::MSG_DEBUG);
        $this->log(Phing::getProperty('line.separator') . $event->getTarget()->getName() . ":",
            Project::MSG_INFO);
        $this->targetStartTime = Phing::currentTimeMillis();
    }

    /** {@inheritDoc}. */
    public function targetFinished(BuildEvent $event)
    {
        $this->log("<< TARGET FINISHED -- " . $event->getTarget()->getName(), Project::MSG_DEBUG);

        $time = $this->formatTime(Phing::currentTimeMillis() - $this->targetStartTime);

        $this->log($event->getTarget()->getName() . ":  duration " . $time, Project::MSG_VERBOSE);
        flush();
    }

    /** {@inheritDoc}. */
    public function taskStarted(BuildEvent $event)
    {
        $this->log(">>> TASK STARTED -- " . $event->getTask()->getTaskName(), Project::MSG_DEBUG);
    }

    /** {@inheritDoc}. */
    public function taskFinished(BuildEvent $event)
    {
        $this->log("<<< TASK FINISHED -- " . $event->getTask()->getTaskName(), Project::MSG_DEBUG);
        $this->flush();
    }

    /** {@inheritDoc}. */
    public function messageLogged(BuildEvent $event)
    {
        $this->log("--- MESSAGE LOGGED", Project::MSG_DEBUG);

        $buf = '';

        if ($event->getTask() != null) {
            $name = $event->getTask()->getTaskName();

            if (!$this->emacsMode) {
                $label = "[" . $name . "] ";
                $size = DefaultLogger::LEFT_COLUMN_SIZE - strlen($label);

                for ($i = 0; $i < $size; $i++) {
                    $buf .= " ";
                }
                $buf .= $label;
            }
        }
        $buf .= $event->getMessage();

        $this->log($buf, $event->getPriority());
    }


    /**
     * The thing that actually sends the information to the output.
     *
     * @param string $mesg The message to log.
     * @param int $level The verbosity level of the message.
     */
    private function log($mesg, $level)
    {
        if ($this->record && ($level <= $this->loglevel) && $this->out != null) {
            $this->out->write($mesg . PHP_EOL);
        }
    }

    private function flush()
    {
        if ($this->record && $this->out != null) {
            $this->out->flush();
        }
    }

    /** {@inheritDoc}. */
    public function setMessageOutputLevel($level)
    {
        if ($level >= Project::MSG_ERR && $level <= Project::MSG_DEBUG) {
            $this->loglevel = $level;
        }
    }

    /** {@inheritDoc}. */
    public function setOutputStream(OutputStream $output)
    {
        $this->closeFile();
        $this->out = $output;
    }

    /** {@inheritDoc}. */
    public function setEmacsMode($emacsMode)
    {
        $this->emacsMode = $emacsMode;
    }

    /** {@inheritDoc}. */
    public function setErrorStream(OutputStream $err)
    {
        $this->setOutputStream($err);
    }

    private static function formatTime($millis)
    {
        $seconds = $millis / 1000;
        $minutes = $seconds / 60;


        if ($minutes > 0) {
            return $minutes . " minute"
            . ($minutes == 1 ? " " : "s ")
            . ($seconds % 60) . " second"
            . ($seconds % 60 == 1 ? "" : "s");
        } else {
            return $seconds . " second"
            . ($seconds % 60 == 1 ? "" : "s");
        }
    }

    /**
     * Set the project associated with this recorder entry.
     *
     * @param Project $project the project instance
     */
    public function setProject(Project $project)
    {
        $this->project = $project;
        if ($this->project != null) {
            $this->project->addBuildListener($this);
        }
    }

    /**
     * Get the project associated with this recorder entry.
     */
    public function getProject()
    {
        return $this->project;
    }

    public function cleanup()
    {
        $this->closeFile();
        if ($this->project != null) {
            $this->project->removeBuildListener($this);
        }
        $this->project = null;
    }

    /**
     * Initially opens the file associated with this recorder.
     * Used by Recorder.
     * @param bool $append Indicates if output must be appended to the logfile or that
     * the logfile should be overwritten.
     * @throws BuildException
     */
    public function openFile($append)
    {
        $this->openFileImpl($append);
    }

    /**
     * Closes the file associated with this recorder.
     * Used by Recorder.
     */
    public function closeFile()
    {
        if ($this->out != null) {
            $this->out->close();
            $this->out = null;
        }
    }

    /**
     * Re-opens the file associated with this recorder.
     * Used by Recorder.
     * @throws BuildException
     */
    public function reopenFile()
    {
        $this->openFileImpl(true);
    }

    private function openFileImpl($append)
    {
        if ($this->out == null) {
            try {
                $this->out = new FileOutputStream($this->filename, $append);
            } catch (IOException $ioe) {
                throw new BuildException("Problems opening file using a recorder entry", $ioe);
            }
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/Task.php';
include_once 'phing/SubBuildListener.php';
include_once 'phing/tasks/system/RecorderEntry.php';

/**
 * Adds a listener to the current build process that records the
 * output to a file.
 * <p>Several recorders can exist at the same time.  Each recorder is
 * associated with a file.  The filename is used as a unique identifier for
 * the recorders.  The first call to the recorder task with an unused filename
 * will create a recorder (using the parameters provided) and add it to the
 * listeners of the build.  All subsequent calls to the recorder task using
 * this filename will modify that recorders state (recording or not) or other
 * properties (like logging level).</p>
 * <p>Some technical issues: the file's print stream is flushed for &quot;finished&quot;
 * events (buildFinished, targetFinished and taskFinished), and is closed on
 * a buildFinished event.</p>
 *
 * @author    Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package   phing.tasks.system
 */
class RecorderTask extends Task implements SubBuildListener
{
    /** The name of the file to record to. */
    private $filename = null;
    /**
     * Whether or not to append. Need Boolean to record an unset state (null).
     */
    private $append = null;
    /**
     * Whether to start or stop recording. Need Boolean to record an unset
     * state (null).
     */
    private $start = null;
    /** The level to log at. A level of -1 means not initialized yet. */
    private $loglevel = -1;
    /** Strip task banners if true.  */
    private $emacsMode = false;

    private $logLevelChoices = array(
        'error'   => 0,
        'warn'    => 1,
        'info'    => 2,
        'verbose' => 3,
        'debug'   => 4
    );

    /**
     * The list of recorder entries.
     * @var RecorderEntry[]
     */
    private static $recorderEntries = array();

    /**
     * Overridden so we can add the task as build listener.
     */
    public function init()
    {
        $this->getProject()->addBuildListener($this);
    }

    /**
     * Sets the name of the file to log to, and the name of the recorder
     * entry.
     *
     * @param string $fname File name of logfile.
     */
    public function setName($fname)
    {
        $this->filename = $fname;
    }

    /**
     * Sets the action for the associated recorder entry.
     *
     * @param string $action The action for the entry to take: start or stop.
     */
    public function setAction($action)
    {
        $this->start = strtolower($action) === "start";
    }


    /**
     * Whether or not the logger should append to a previous file.
     * @param bool $append if true, append to a previous file.
     */
    public function setAppend($append)
    {
        $this->append = (bool) $append;
    }


    /**
     * Set emacs mode.
     * @param bool $emacsMode if true use emacs mode
     */
    public function setEmacsMode($emacsMode)
    {
        $this->emacsMode = $emacsMode;
    }

    /**
     * Sets the level to which this recorder entry should log to.
     * @param string $level the level to set.
     */
    public function setLoglevel($level)
    {
        $this->loglevel = $level;
    }

    /**
     * The main execution.
     * @throws BuildException on error
     */
    public function main()
    {
        if ($this->filename == null) {
            throw new BuildException("No filename specified");
        }

        $this->getProject()->log("setting a recorder for name " . $this->filename, Project::MSG_DEBUG);

        // get the recorder entry
        $recorder = $this->getRecorder($this->filename, $this->getProject());
        // set the values on the recorder
        if ($this->loglevel === -1) {
            $recorder->setMessageOutputLevel($this->loglevel);
        } elseif (isset($this->logLevelChoices[$this->loglevel])) {
            $recorder->setMessageOutputLevel($this->logLevelChoices[$this->loglevel]);
        } else {
            throw new BuildException('Loglevel should be one of (error|warn|info|verbose|debug).');
        }

        $recorder->setEmacsMode(StringHelper::booleanValue($this->emacsMode));
        if ($this->start != null) {
            if (StringHelper::booleanValue($this->start)) {
                $recorder->reopenFile();
                $recorder->setRecordState($this->start);
            } else {
                $recorder->setRecordState($this->start);
                $recorder->closeFile();
            }
        }
    }

    /**
     * Gets the recorder that's associated with the passed in name. If the
     * recorder doesn't exist, then a new one is created.
     * @param string $name the name of the recorder
     * @param Project $proj the current project
     * @return RecorderEntry a recorder
     * @throws BuildException on error
     */
    protected function getRecorder($name, Project $proj)
    {
            // create a recorder entry
            $entry = isset(self::$recorderEntries[$name]) ? self::$recorderEntries[$name] : new RecorderEntry($name);

            if ($this->append == null) {
                $entry->openFile(false);
            } else {
                $entry->openFile(StringHelper::booleanValue($this->append));
            }
            $entry->setProject($proj);
            self::$recorderEntries[$name] = $entry;

        return $entry;
    }

    /**
     * Empty implementation required by SubBuildListener interface.
     * @param BuildEvent $event ignored.
     */
    public function buildStarted(BuildEvent $event)
    {
    }

    /**
     * Empty implementation required by SubBuildListener interface.
     * @param BuildEvent $event ignored.
     */
    public function subBuildStarted(BuildEvent $event)
    {
    }

    /**
     * Empty implementation required by SubBuildListener interface.
     * @param BuildEvent $event ignored.
     */
    public function targetStarted(BuildEvent $event)
    {
    }

    /**
     * Empty implementation required by SubBuildListener interface.
     * @param BuildEvent $event ignored.
     */
    public function targetFinished(BuildEvent $event)
    {
    }

    /**
     * Empty implementation required by SubBuildListener interface.
     * @param BuildEvent $event ignored.
     */
    public function taskStarted(BuildEvent $event)
    {
    }

    /**
     * Empty implementation required by SubBuildListener interface.
     * @param BuildEvent $event ignored.
     */
    public function taskFinished(BuildEvent $event)
    {
    }

    /**
     * Empty implementation required by SubBuildListener interface.
     * @param BuildEvent $event ignored.
     */
    public function messageLogged(BuildEvent $event)
    {
    }

    /**
     * Cleans recorder registry.
     * @param BuildEvent $event ignored.
     */
    public function buildFinished(BuildEvent $event)
    {
        $this->cleanup();
    }

    /**
     * Cleans recorder registry, if this is the subbuild the task has
     * been created in.
     * @param BuildEvent $event ignored.
     */
    public function subBuildFinished(BuildEvent $event)
    {
        if ($event->getProject() == $this->getProject()) {
            $this->cleanup();
        }
    }

    /**
     * cleans recorder registry and removes itself from BuildListener list.
     */
    private function cleanup()
    {
        $entries = self::$recorderEntries;
        foreach ($entries as $key => $entry) {
            if ($entry->getProject() == $this->getProject()) {
                unset(self::$recorderEntries[$key]);
            }
        }
        $this->getProject()->removeBuildListener($this);
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * This task is for using filter chains to make changes to files and overwrite the original files.
 *
 * This task was created to serve the need for "cleanup" tasks -- e.g. a ReplaceRegexp task or strip task
 * being used to modify files and then overwrite the modified files.  In many (most?) cases you probably
 * should just use a copy task  to preserve the original source files, but this task supports situations
 * where there is no src vs. build directory, and modifying source files is actually desired.
 *
 * <code>
 *    <reflexive>
 *        <fileset dir=".">
 *            <include pattern="*.html">
 *        </fileset>
 *        <filterchain>
 *            <replaceregexp>
 *                <regexp pattern="\n\r" replace="\n"/>
 *            </replaceregexp>
 *        </filterchain>
 *    </reflexive>
 * </code>
 *
 * @author    Hans Lellelid <hans@xmpl.org>
 *
 * @package   phing.tasks.system
 */
class ReflexiveTask extends Task
{

    /** Single file to process. */
    private $file;

    /**
     * Any filesets that should be processed.
     *
     * @var FileSet[]
     */
    private $filesets = array();

    /**
     * Any filters to be applied before append happens.
     *
     * @var FilterChain[]
     */
    private $filterChains = array();

    /** Alias for setFrom()
     * @param PhingFile $f
     */
    public function setFile(PhingFile $f)
    {
        $this->file = $f;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Creates a filterchain
     *
     * @return FilterChain The created filterchain object
     */
    public function createFilterChain()
    {
        $num = array_push($this->filterChains, new FilterChain($this->project));

        return $this->filterChains[$num - 1];
    }

    /** Append the file(s). */
    public function main()
    {

        if ($this->file === null && empty($this->filesets)) {
            throw new BuildException("You must specify a file or fileset(s) for the <reflexive> task.");
        }

        // compile a list of all files to modify, both file attrib and fileset elements
        // can be used.

        $files = array();

        if ($this->file !== null) {
            $files[] = $this->file;
        }

        if (!empty($this->filesets)) {
            foreach ($this->filesets as $fs) {
                try {
                    $ds = $fs->getDirectoryScanner($this->project);
                    $filenames = $ds->getIncludedFiles(); // get included filenames
                    $dir = $fs->getDir($this->project);
                    foreach ($filenames as $fname) {
                        $files[] = new PhingFile($dir, $fname);
                    }
                } catch (BuildException $be) {
                    $this->log($be->getMessage(), Project::MSG_WARN);
                }
            }
        }

        $this->log("Applying reflexive processing to " . count($files) . " files.");

        // These "slots" allow filters to retrieve information about the currently-being-process files
        $slot = $this->getRegisterSlot("currentFile");
        $basenameSlot = $this->getRegisterSlot("currentFile.basename");

        foreach ($files as $file) {
            // set the register slots

            $slot->setValue($file->getPath());
            $basenameSlot->setValue($file->getName());

            // 1) read contents of file, pulling through any filters
            $in = null;
            try {
                $contents = "";
                $in = FileUtils::getChainedReader(new FileReader($file), $this->filterChains, $this->project);
                while (-1 !== ($buffer = $in->read())) {
                    $contents .= $buffer;
                }
                $in->close();
            } catch (Exception $e) {
                if ($in) {
                    $in->close();
                }
                $this->log("Erorr reading file: " . $e->getMessage(), Project::MSG_WARN);
            }

            try {
                // now create a FileWriter w/ the same file, and write to the file
                $out = new FileWriter($file);
                $out->write($contents);
                $out->close();
                $this->log("Applying reflexive processing to " . $file->getPath(), Project::MSG_VERBOSE);
            } catch (Exception $e) {
                if ($out) {
                    $out->close();
                }
                $this->log("Error writing file back: " . $e->getMessage(), Project::MSG_WARN);
            }

        }
    }
}
<?php
/*
 *  $Id: 8a07480e1e162ecc15ba72b942937d9f6a3fecd8 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Task for resolving relative paths and setting absolute path in property value.
 *
 * This task was created to address a need for resolving absolute paths of files / directories.
 * In many cases a relative directory (e.g. "./build") is specified, but it needs to be treated
 * as an absolute path since other build files (e.g. in subdirs) should all be using the same
 * path -- and not treating it as a relative path to their own directory.
 *
 * <code>
 * <property name="relative_path" value="./dirname"/>
 * <resolvepath propertyName="absolute_path" file="${relative_path}"/>
 * <echo>Resolved [absolute] path: ${absolute_path}</echo>
 * </code>
 *
 * TODO:
 *      - Possibly integrate this with PackageAsPath, for handling/resolving dot-path paths.
 *
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: 8a07480e1e162ecc15ba72b942937d9f6a3fecd8 $
 * @package   phing.tasks.system
 */
class ResolvePathTask extends Task
{

    /** Name of property to set. */
    private $propertyName;

    /** The [possibly] relative file/path that needs to be resolved. */
    private $file;

    /** Base directory used for resolution.
     * @var PhingFile
     */
    private $dir;

    /**
     * Log level
     */
    private $logLevel = Project::MSG_VERBOSE;

    /**
     * Set the name of the property to set.
     * @param  string $v Property name
     * @return void
     */
    public function setPropertyName($v)
    {
        $this->propertyName = $v;
    }

    /**
     * Sets a base dir to use for resolution.
     * @param PhingFile $d
     */
    public function setDir(PhingFile $d)
    {
        $this->dir = $d;
    }

    /**
     * Sets a path (file or directory) that we want to resolve.
     * This is the same as setFile() -- just more generic name so that it's
     * clear that you can also use it to set directory.
     * @param string $f
     * @see setFile()
     */
    public function setPath($f)
    {
        $this->file = $f;
    }

    /**
     * Sets a file that we want to resolve.
     * @param string $f
     */
    public function setFile($f)
    {
        $this->file = $f;
    }

    /**
     * Set level of log messages generated (default = verbose)
     *
     * @param string $level Log level
     *
     * @throws BuildException
     * @return void
     */
    public function setLevel($level)
    {
        switch ($level) {
            case 'error':
                $this->logLevel = Project::MSG_ERR;
                break;
            case 'warning':
                $this->logLevel = Project::MSG_WARN;
                break;
            case 'info':
                $this->logLevel = Project::MSG_INFO;
                break;
            case 'verbose':
                $this->logLevel = Project::MSG_VERBOSE;
                break;
            case 'debug':
                $this->logLevel = Project::MSG_DEBUG;
                break;
            default:
                throw new BuildException(
                    sprintf('Unknown log level "%s"', $level)
                );
        }
    }

    /**
     * Perform the resolution & set property.
     */
    public function main()
    {
        if (!$this->propertyName) {
            throw new BuildException("You must specify the propertyName attribute", $this->getLocation());
        }

        // Currently only files are supported
        if ($this->file === null) {
            throw new BuildException("You must specify a path to resolve", $this->getLocation());
        }

        $fs = FileSystem::getFileSystem();

        // if dir attribute was specified then we should
        // use that as basedir to which file was relative.
        // -- unless the file specified is an absolute path
        if ($this->dir !== null && !$fs->isAbsolute(new PhingFile($this->file))) {
            $this->file = new PhingFile($this->dir->getPath(), $this->file);
        }

        $resolved = $this->project->resolveFile($this->file);

        $this->log("Resolved " . $this->file . " to " . $resolved->getAbsolutePath(), $this->logLevel);
        $this->project->setProperty($this->propertyName, $resolved->getAbsolutePath());
    }

}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/BuildException.php';
require_once 'phing/Project.php';
require_once 'phing/Task.php';
require_once 'phing/TaskContainer.php';

/**
 * Retries the nested task a set number of times.
 *
 * @author  Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.tasks.system
 */
class Retry extends Task implements TaskContainer
{
    /**
     * Task to execute n times.
     *
     * @var Task $nestedTask
     */
    private $nestedTask;

    /**
     * Set retry count to 1 by default.
     *
     * @var int $retryCount
     */
    private $retryCount = 1;

    /**
     * The time to wait between retries in seconds, default to 0.
     *
     * @var int $retryDelay
     */
    private $retryDelay = 0;

    /**
     * Set the task.
     *
     * @param Task $t the task to retry.
     *
     * @throws BuildException
     */
    public function addTask(Task $t)
    {
        if ($this->nestedTask !== null) {
            throw new BuildException(
                'The retry task container accepts a single nested task'
                . ' (which may be a sequential task container)'
            );
        }
        $this->nestedTask = $t;
    }

    /**
     * Set the number of times to retry the task.
     *
     * @param int $n the number to use.
     */
    public function setRetryCount($n)
    {
        $this->retryCount = $n;
    }

    /**
     * Set the delay between retries (in seconds).
     *
     * @param int $retryDelay the time between retries.
     *
     * @throws BuildException
     */
    public function setRetryDelay($retryDelay)
    {
        if ($retryDelay < 0) {
            throw new BuildException(
                'retryDelay must be a non-negative number'
            );
        }
        $this->retryDelay = $retryDelay;
    }

    /**
     * Perform the work.
     *
     * @throws BuildException if there is an error.
     */
    public function main()
    {
        $errorMessages = '';
        for ($i = 0; $i <= $this->retryCount; $i++) {
            try {
                $this->nestedTask->perform();
                break;
            } catch (Exception $e) {
                $errorMessages .= $e->getMessage();
                if ($i >= $this->retryCount) {
                    $taskName = $this->nestedTask->getTaskName();
                    $exceptionMessage = <<<EXCEPTION_MESSAGE
Task [{$taskName}] failed after [{$this->retryCount}] attempts; giving up
Error messages:
$errorMessages
EXCEPTION_MESSAGE;
                    throw new BuildException(
                        $exceptionMessage,
                        $this->getLocation()
                    );
                }

                if ($this->retryDelay > 0) {
                    $msg = sprintf(
                        'Attempt [%s]: error occurred; retrying after %s s...',
                        $i,
                        $this->retryDelay);
                } else {
                    $msg = sprintf(
                        'Attempt [%s]: error occurred; retrying...',
                        $i
                    );
                }

                $this->log($msg, Project::MSG_INFO);
                $errorMessages .= PHP_EOL;

                if ($this->retryDelay > 0) {
                    sleep($this->retryDelay);
                }
            }
        }
    }
}
<?php

/*
 *  $Id: 811cc87eb5f11dc6d7833b1fc89a5193ceb268dd $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/TaskContainer.php';

/**
 * Sequential is a container task that contains other Phing Task objects.
 *
 * The sequential task has no attributes and does not support any nested
 * elements apart from Ant tasks. Any valid Ant task may be embedded within the
 * sequential task.
 *
 * @since 2.1.2
 * @package phing.tasks.system
 */
class SequentialTask extends Task implements TaskContainer
{

    /** Optional Vector holding the nested tasks */
    protected $nestedTasks = array();

    /**
     * Add a nested task to Sequential.
     * @param Task $nestedTask Nested task to execute Sequential
     */
    public function addTask(Task $nestedTask)
    {
        $this->nestedTasks[] = $nestedTask;
    }

    /**
     * Execute all nestedTasks.
     * @throws BuildException if one of the nested tasks fails.
     */
    public function main()
    {
        foreach ($this->nestedTasks as $task) {
            $task->perform();
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * A phing sleep task.
 *
 * <p>A task for sleeping a short period of time, useful when a
 * build or deployment process requires an interval between tasks.</p>
 *
 * <p>A negative value can be supplied to any of attributes provided the total sleep time
 * is positive, pending fundamental changes in physics and PHP execution times.</p>
 *
 * <p>Note that sleep times are always hints to be interpreted by the OS how it feels
 * small times may either be ignored or rounded up to a minimum timeslice. Note
 * also that the system clocks often have a fairly low granularity too, which complicates
 * measuring how long a sleep actually took.</p>
 *
 * @author   Daniel Kutik, daniel@kutik.eu
 * @version  $Id: 95d0328752cfbcc4e3ed9e3f003d942299e5d4d0 $
 * @package  phing.tasks.system
 */
class SleepTask extends Task
{
    /**
     * failure flag
     * @var bool
     */
    private $failOnError = true;

    /**
     * sleep seconds
     */
    private $seconds = 0;

    /**
     * sleep hours
     */
    private $hours = 0;
    /**
     * sleep minutes
     */
    private $minutes = 0;

    /**
     * sleep milliseconds
     */
    private $milliseconds = 0;

    /**
     * @param string $var
     */
    public function setFailOnError($var)
    {
        if (is_string($var)) {
            $var = strtolower($var);
            $this->failOnError = ($var === 'yes' || $var === 'true');
        } else {
            $this->failOnError = (bool) $var;
        }
    }

    /**
     * @return bool
     */
    public function getFailOnError()
    {
        return $this->failOnError;
    }

    /**
     * @param mixed $hours
     */
    public function setHours($hours)
    {
        $this->hours = $hours;
    }

    /**
     * @return mixed
     */
    public function getHours()
    {
        return $this->hours;
    }

    /**
     * @param mixed $milliseconds
     */
    public function setMilliseconds($milliseconds)
    {
        $this->milliseconds = $milliseconds;
    }

    /**
     * @return mixed
     */
    public function getMilliseconds()
    {
        return $this->milliseconds;
    }

    /**
     * @param mixed $minutes
     */
    public function setMinutes($minutes)
    {
        $this->minutes = $minutes;
    }

    /**
     * @return mixed
     */
    public function getMinutes()
    {
        return $this->minutes;
    }

    /**
     * @param mixed $seconds
     */
    public function setSeconds($seconds)
    {
        $this->seconds = $seconds;
    }

    /**
     * @return mixed
     */
    public function getSeconds()
    {
        return $this->seconds;
    }

    /**
     * return time to sleep
     *
     * @return int time. if below 0 then there is an error
     */
    private function getSleepTime()
    {
        return ((($this->hours * 60) + $this->minutes) * 60 + $this->seconds) * 1000 + $this->milliseconds;
    }

    /**
     * verify parameters
     *
     * @throws BuildException if something is invalid
     */
    private function validateAttributes()
    {
        if ($this->getSleepTime() < 0) {
            throw new BuildException('Negative sleep periods are not supported');
        }
    }

    public function main()
    {
        try {
            $this->validateAttributes();
            $sleepTime = $this->getSleepTime();
            usleep($sleepTime * 1000);
        } catch (Exception $e) {
            if ($this->failOnError) {
                throw new BuildException($e);
            }
        }
    }
}
<?php
/**
 *  $Id: 8eb3563703d50b210a90b2847d7446698b4e9c43 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/Task.php';
require_once 'phing/tasks/system/SequentialTask.php';

/**
 * Task definition for the phing task to switch on a particular value.
 *
 * Task calling syntax:
 * ```
 * <switch value="value" [caseinsensitive="true|false"] >
 *   <case value="val">
 *     <property name="propname" value="propvalue" /> |
 *     <phingcall target="targetname" /> |
 *     any other tasks
 *   </case>
 *   [
 *   <default>
 *     <property name="propname" value="propvalue" /> |
 *     <phingcall target="targetname" /> |
 *     any other tasks
 *   </default>
 *   ]
 * </switch>
 * ```
 *
 *
 * Attributes:
 * value           -> The value to switch on
 * caseinsensitive -> Should we do case insensitive comparisons?
 *                    (default is false)
 *
 * Subitems:
 * case     --> An individual case to consider, if the value that
 *              is being switched on matches to value attribute of
 *              the case, then the nested tasks will be executed.
 * default  --> The default case for when no match is found.
 *
 *
 * Crude Example:
 *
 * ```
 * <switch value="${foo}">
 *     <case value="bar">
 *       <echo message="The value of property foo is bar" />
 *     </case>
 *     <case value="baz">
 *       <echo message="The value of property foo is baz" />
 *     </case>
 *     <default>
 *       <echo message="The value of property foo is not sensible" />
 *     </default>
 * </switch>
 * ```
 *
 * @author Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.tasks.system
 */
class SwitchTask extends Task
{
    /** @var mixed $value */
    private $value = null;

    /** @var array $cases */
    private $cases = array();

    /** @var SequentialTask $defaultCase */
    private $defaultCase = null;

    /** @var bool $caseInsensitive */
    private $caseInsensitive = false;

    /***
     * Sets the value being switched on.
     *
     * @param mixed $value
     *
     * @return void
     */
    public function setValue($value)
    {
        $this->value = $value;
    }

    /**
     * Adds a CaseTask.
     *
     * @param CaseTask $case
     *
     * @return void
     */
    public function addCase(CaseTask $case)
    {
        $this->cases[] = $case;
    }

    /**
     * @param $bool
     *
     * @return void
     */
    public function setCaseInsensitive($bool)
    {
        $this->caseInsensitive = $bool;
    }

    /**
     * Creates the `<default>` tag
     */
    public function addDefault(SequentialTask $res)
    {
        if ($this->defaultCase !== null) {
            throw new BuildException("Cannot specify multiple default cases");
        }

        $this->defaultCase = $res;
    }

    public function main()
    {
        if ($this->value === null) {
            throw new BuildException("Value is missing <switch>");
        }

        if (empty($this->cases) && $this->defaultCase === null) {
            throw new BuildException("No cases supplied <switch>");
        }

        $selectedCase = $this->defaultCase;

        /** @var CaseTask $case */
        foreach ($this->cases as $case) {
            $cValue = $case->getValue();

            if (empty($case)) {
                throw new BuildException("Value is required for case.");
            }

            $mValue = $this->value;
            if ($this->caseInsensitive) {
                $cValue = strtoupper($case->getValue());
                $mValue = strtoupper($this->value);
            }

            if ($cValue === $mValue && $case != $this->defaultCase) {
                $selectedCase = $case;
            }
        }

        if ($selectedCase === null) {
            throw new BuildException("No case matched the value " . $this->value . " and no default has been specified.");
        }

        $selectedCase->perform();
    }
}

/**
 * "Inner" class for SwitchTask.
 *
 * @package phing.tasks.system
 */
class CaseTask extends SequentialTask
{
    /** @var mixed $value */
    private $value = null;

    /**
     * @param $value
     *
     * @return void
     */
    public function setValue($value)
    {
        $this->value = $value;
    }

    /**
     * @return mixed
     */
    public function getValue()
    {
        return $this->value;
    }

    /**
     * {@inheritdoc}
     */
    public function main()
    {
        /** @var Task $task */
        foreach ($this->nestedTasks as $task) {
            $task->perform();
        }
    }
}
<?php

/*
 * $Id: b554ecb4f867f8ccbcd764de62482d6c16800c00 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/system/io/PhingFile.php';

/**
 * Register a task for use within a buildfile.
 *
 * This is for registering your own tasks -- or any non-core Task -- for use within a buildfile.
 * If you find that you are using a particular class frequently, you may want to edit the
 * phing/tasks/defaults.properties file so that it is included by default. You may also
 * want to submit it (if LGPL or compatible license) to be included in Phing distribution.
 *
 * <pre>
 *   <taskdef name="mytag" classname="path.to.MyHandlingClass"/>
 *   .
 *   .
 *   <mytag param1="val1" param2="val2"/>
 * </pre>
 *
 * TODO:
 *    -- possibly refactor since this is almost the same as TypeDefTask
 *      (right now these are just too simple to really justify creating an abstract class)
 *
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: b554ecb4f867f8ccbcd764de62482d6c16800c00 $
 * @package   phing.tasks.system
 */
class TaskdefTask extends Task
{

    /** Tag name for task that will be used in XML */
    private $name;

    /**
     * Classname of task to register.
     * This can be a dot-path -- relative to a location on PHP include_path.
     * E.g. path.to.MyClass ->  path/to/MyClass.php
     * @var string
     */
    private $classname;

    /**
     * Path to add to PHP include_path to aid in finding specified class.
     * @var Path
     */
    private $classpath;

    /**
     * Refid to already defined classpath
     */
    private $classpathId;

    /**
     * Name of file to load multiple definitions from.
     * @var string
     */
    private $typeFile;

    /**
     * Set the classpath to be used when searching for component being defined
     *
     * @param Path $classpath A Path object containing the classpath.
     */
    public function setClasspath(Path $classpath)
    {
        if ($this->classpath === null) {
            $this->classpath = $classpath;
        } else {
            $this->classpath->append($classpath);
        }
    }

    /**
     * Create the classpath to be used when searching for component being defined
     *
     * @return Path
     */
    public function createClasspath()
    {
        if ($this->classpath === null) {
            $this->classpath = new Path($this->project);
        }

        return $this->classpath->createPath();
    }

    /**
     * Reference to a classpath to use when loading the files.
     * @param Reference $r
     */
    public function setClasspathRef(Reference $r)
    {
        $this->classpathId = $r->getRefId();
        $this->createClasspath()->setRefid($r);
    }

    /**
     * Sets the name that will be used in XML buildfile.
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Sets the class name / dotpath to use.
     * @param string $class
     */
    public function setClassname($class)
    {
        $this->classname = $class;
    }

    /**
     * Sets the file of definitionas to use to use.
     * @param string $file
     */
    public function setFile($file)
    {
        $this->typeFile = $file;
    }

    /** Main entry point */
    public function main()
    {
        if ($this->typeFile === null &&
            ($this->name === null || $this->classname === null)
        ) {
            throw new BuildException("You must specify name and class attributes for <taskdef>.");
        }
        if ($this->typeFile == null) {
            $this->log("Task " . $this->name . " will be handled by class " . $this->classname, Project::MSG_VERBOSE);
            $this->project->addTaskDefinition($this->name, $this->classname, $this->classpath);
        } else {
            try { // try to load taskdefs given in file
                $props = new Properties();
                $in = new PhingFile((string) $this->typeFile);

                if ($in === null) {
                    throw new BuildException("Can't load task list {$this->typeFile}");
                }
                $props->load($in);

                $enum = $props->propertyNames();
                foreach ($enum as $key) {
                    $value = $props->getProperty($key);
                    $this->project->addTaskDefinition($key, $value, $this->classpath);
                }
            } catch (IOException $ioe) {
                throw new BuildException("Can't load task list {$this->typeFile}");
            }
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/Task.php';
include_once 'phing/system/io/PhingFile.php';
include_once 'phing/BuildException.php';

/**
 * This task sets a property to the name of a temporary file.
 * Unlike {@link PhingFile::createTempFile()}, this task does not (by default) actually create the
 * temporary file, but it does guarantee that the file did not
 * exist when the task was executed.
 *
 * Examples:
 *
 * `<tempfile property="temp.file" />`
 *
 * create a temporary file
 *
 * `<tempfile property="temp.file" suffix=".xml" />`
 *
 * create a temporary file with the .xml suffix.
 *
 * `<tempfile property="temp.file" destDir="build"/>`
 *
 * create a temp file in the build subdir
 *
 * @author  Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.tasks.system
 */
class TempFile extends Task
{
    /**
     * Name of property to set.
     */
    private $property = '';

    /**
     * Directory to create the file in. Can be null.
     */
    private $destDir;

    /**
     * Prefix for the file.
     */
    private $prefix;

    /**
     * Suffix for the file.
     */
    private $suffix = '';

    /** deleteOnExit flag */
    private $deleteOnExit;

    /** createFile flag */
    private $createFile;

    /**
     * Sets the property you wish to assign the temporary file to.
     *
     * @param string $property The property to set
     */
    public function setProperty($property)
    {
        $this->property = $property;
    }

    /**
     * Sets the destination directory. If not set,
     * the basedir directory is used instead.
     *
     * @param string|PhingFile $destDir The new destDir value
     */
    public function setDestDir($destDir)
    {
        if ($destDir instanceof PhingFile) {
            $this->destDir = $destDir;
        } else  {
            $this->destDir = new PhingFile($destDir);
        }
    }


    /**
     * Sets the optional prefix string for the temp file.
     *
     * @param string $prefix string to prepend to generated string
     */
    public function setPrefix($prefix)
    {
        $this->prefix = $prefix;
    }


    /**
     * Sets the optional suffix string for the temp file.
     *
     * @param string $suffix suffix including any "." , e.g ".xml"
     */
    public function setSuffix($suffix)
    {
        $this->suffix = $suffix;
    }

    /**
     * Set whether the tempfile created by this task should be set
     * for deletion on normal VM exit.
     * @param boolean $deleteOnExit boolean flag.
     */
    public function setDeleteOnExit($deleteOnExit)
    {
        $this->deleteOnExit = $deleteOnExit;
    }

    /**
     * Learn whether deleteOnExit is set for this tempfile task.
     * @return boolean deleteOnExit flag.
     */
    public function isDeleteOnExit()
    {
        return $this->deleteOnExit;
    }

    /**
     * If set the file is actually created, if not just a name is created.
     * @param boolean $createFile boolean flag.
     */
    public function setCreateFile($createFile)
    {
        $this->createFile = $createFile;
    }

    /**
     * Learn whether createFile flag is set for this tempFile task.
     * @return boolean the createFile flag.
     */
    public function isCreateFile()
    {
        return $this->createFile;
    }

    /**
     * Creates the temporary file.
     *
     * @throws BuildException if something goes wrong with the build
     */
    public function main()
    {
        if ($this->property === '') {
            throw new BuildException('no property specified');
        }
        if ($this->destDir === null) {
            $this->destDir = $this->getProject()->resolveFile('.');
        }
        $fu = new FileUtils;
        $tmpFile = $fu->createTempFile(
            $this->prefix,
            $this->suffix,
            $this->destDir,
            $this->deleteOnExit,
            $this->createFile
        );
        $this->getProject()->setNewProperty($this->property, $tmpFile->toString());
    }
}
<?php
/*
 *  $Id: 8269fd7fedc201ff6bd3bd61b0d902f80152c674 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/util/DirectoryScanner.php';
include_once 'phing/types/FileSet.php';
include_once 'phing/util/FileUtils.php';
include_once 'phing/system/io/PhingFile.php';
include_once 'phing/system/io/IOException.php';

/**
 * Touch a file and/or fileset(s); corresponds to the Unix touch command.
 *
 * If the file to touch doesn't exist, an empty one is created.
 *
 * @version $Id: 8269fd7fedc201ff6bd3bd61b0d902f80152c674 $
 * @package phing.tasks.system
 */
class TouchTask extends Task
{
    /** @var PhingFile $file */
    private $file;
    private $millis = -1;
    private $dateTime;
    private $filesets = array();
    private $fileUtils;
    private $mkdirs = false;
    private $verbose = true;

    /**
     *
     */
    public function __construct()
    {
        $this->fileUtils = new FileUtils();
    }

    /**
     * Sets a single source file to touch.  If the file does not exist
     * an empty file will be created.
     * @param PhingFile $file
     * @return void
     */
    public function setFile(PhingFile $file)
    {
        $this->file = $file;
    }

    /**
     * the new modification time of the file
     * in milliseconds since midnight Jan 1 1970.
     * Optional, default=now
     * @param $millis
     * @return void
     */
    public function setMillis($millis)
    {
        $this->millis = (int) $millis;
    }

    /**
     * the new modification time of the file
     * in the format MM/DD/YYYY HH:MM AM or PM;
     * Optional, default=now
     * @param $dateTime
     * @return void
     */
    public function setDatetime($dateTime)
    {
        $this->dateTime = (string) $dateTime;
    }

    /**
     * Set whether nonexistent parent directories should be created
     * when touching new files.
     * @param boolean $mkdirs whether to create parent directories.
     */
    public function setMkdirs($mkdirs)
    {
        $this->mkdirs = $mkdirs;
    }

    /**
     * Set whether the touch task will report every file it creates;
     * defaults to <code>true</code>.
     * @param boolean $verbose flag.
     */
    public function setVerbose($verbose)
    {
        $this->verbose = $verbose;
    }

    /**
     * Nested adder, adds a set of files (nested fileset attribute).
     *
     * @param FileSet $fs
     * @return void
     */
    public function addFileSet(FileSet $fs)
    {
        $this->filesets[] = $fs;
    }

    /**
     * Execute the touch operation.
     * @throws BuildException
     */
    public function main()
    {
        $savedMillis = $this->millis;

        if ($this->file === null && count($this->filesets) === 0) {
            throw new BuildException("Specify at least one source - a file or a fileset.");
        }

        if ($this->file !== null && $this->file->exists() && $this->file->isDirectory()) {
            throw new BuildException("Use a fileset to touch directories.");
        }

        try { // try to touch file
            if ($this->dateTime !== null) {
                $this->setMillis(strtotime($this->dateTime));
                if ($this->millis < 0) {
                    throw new BuildException("Date of {$this->dateTime} results in negative milliseconds value relative to epoch (January 1, 1970, 00:00:00 GMT).");
                }
            }
            $this->_touch();
        } catch (Exception $ex) {
            throw new BuildException("Error touch()ing file", $ex, $this->location);
        }

        $this->millis = $savedMillis;

    }

    /**
     * Does the actual work.
     */
    public function _touch()
    {
        if ($this->file !== null) {
            if (!$this->file->exists()) {
                $this->log("Creating " . $this->file->__toString(), $this->verbose ? Project::MSG_INFO : Project::MSG_VERBOSE);
                try { // try to create file
                    $this->file->createNewFile($this->mkdirs);
                } catch (IOException  $ioe) {
                    throw new BuildException("Error creating new file " . $this->file->__toString(
                        ), $ioe, $this->location);
                }
            }
        }

        $resetMillis = false;
        if ($this->millis < 0) {
            $resetMillis = true;
            $this->millis = Phing::currentTimeMillis();
        }

        if ($this->file !== null) {
            $this->touchFile($this->file);
        }

        // deal with the filesets
        foreach ($this->filesets as $fs) {

            $ds = $fs->getDirectoryScanner($this->getProject());
            $fromDir = $fs->getDir($this->getProject());

            $srcFiles = $ds->getIncludedFiles();
            $srcDirs = $ds->getIncludedDirectories();

            for ($j = 0, $_j = count($srcFiles); $j < $_j; $j++) {
                $this->touchFile(new PhingFile($fromDir, (string) $srcFiles[$j]));
            }

            for ($j = 0, $_j = count($srcDirs); $j < $_j; $j++) {
                $this->touchFile(new PhingFile($fromDir, (string) $srcDirs[$j]));
            }
        }

        if ($resetMillis) {
            $this->millis = -1;
        }
    }

    /**
     * @param $file
     * @throws BuildException
     */
    private function touchFile($file)
    {
        if (!$file->canWrite()) {
            throw new BuildException("Can not change modification date of read-only file " . $file->__toString());
        }
        $file->setLastModified($this->millis);
    }

}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */
include_once 'phing/Task.php';
include_once 'phing/system/io/PhingFile.php';
include_once 'phing/BuildException.php';

/**
 * @author  Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.tasks.system
 */
class TruncateTask extends Task
{
    private $create = true;
    private $mkdirs = false;

    private $length;
    private $adjust;
    private $file;

    /**
     * Set a single target File.
     * @param PhingFile|string $f the single File
     * @throws \IOException
     * @throws \NullPointerException
     */
    public function setFile($f)
    {
        if (is_string($f)) {
            $f = new PhingFile($f);
        }
        $this->file = $f;
    }

    /**
     * Set the amount by which files' lengths should be adjusted.
     * It is permissible to append K / M / G / T / P.
     * @param $adjust (positive or negative) adjustment amount.
     */
    public function setAdjust($adjust)
    {
        $this->adjust = $adjust;
    }

    /**
     * Set the length to which files should be set.
     * It is permissible to append K / M / G / T / P.
     * @param $length (positive) adjustment amount.
     *
     * @throws \BuildException
     */
    public function setLength($length)
    {
        $this->length = $length;
        if ($this->length !== null && $this->length < 0) {
            throw new BuildException('Cannot truncate to length ' . $this->length);
        }
    }

    /**
     * Set whether to create nonexistent files.
     * @param boolean $create default <code>true</code>.
     */
    public function setCreate($create)
    {
        $this->create = $create;
    }

    /**
     * Set whether, when creating nonexistent files, nonexistent directories
     * should also be created.
     * @param boolean $mkdirs default <code>false</code>.
     */
    public function setMkdirs($mkdirs)
    {
        $this->mkdirs = $mkdirs;
    }

    /**
     * {@inheritDoc}.
     * @throws \BuildException
     */
    public function main()
    {
        if ($this->length !== null && $this->adjust !== null) {
            throw new BuildException(
                'length and adjust are mutually exclusive options'
            );
        }
        if ($this->length === null && $this->adjust === null) {
            $this->length = 0;
        }
        if ($this->file === null) {
            throw new BuildException('No files specified.');
        }

        if ($this->shouldProcess($this->file)) {
            $this->process($this->file);
        }
    }

    /**
     * @param PhingFile $f
     * @return bool
     * @throws \BuildException
     */
    private function shouldProcess(PhingFile $f)
    {
        if ($f->isFile()) {
            return true;
        }
        if (!$this->create) {
            return false;
        }
        $exception = null;
        try {
            /** @var PhingFile $parent */
            $parent = $f->getParentFile();
            if ($this->mkdirs && !$parent->exists()) {
                $parent->mkdirs();
            }

            if ($f->createNewFile()) {
                return true;
            }
        } catch (IOException $e) {
            $exception = $e;
        }
        $msg = "Unable to create " . $f;
        if ($exception === null) {
            $this->log($msg, Project::MSG_WARN);
            return false;
        }
        throw new BuildException($msg, $exception);
    }

    private function process(PhingFile $f)
    {
        $len = $f->length();
        $newLength = $this->length === null
            ? $len + $this->adjust
            : $this->length;

        if ($len === $newLength) {
            //nothing to do!
            return;
        }

        $splFile = new SplFileObject($f->getPath(), 'a+');

        if (!$splFile->ftruncate((int) $newLength)) {
            throw new BuildException("Exception working with " . (string)$splFile);
        }

        $splFile->rewind();
        clearstatcache();
    }
}
<?php
/*
 *  $Id: 2059a94a0c48659f29e244115f17cae9e0b4b65b $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/Task.php';

/**
 * A wrapper task that lets you run tasks(s) when another set
 * of tasks fails.
 *
 * Inspired by {@link http://ant-contrib.sourceforge.net/tasks/tasks/trycatch.html}
 *
 * @author   Michiel Rook <mrook@php.net>
 * @version  $Id: 2059a94a0c48659f29e244115f17cae9e0b4b65b $
 * @package  phing.tasks.system
 */
class TryCatchTask extends Task
{
    protected $propertyName = "";
    protected $referenceName = '';

    protected $tryContainer = null;
    protected $catchContainer = null;
    protected $finallyContainer = null;

    /**
     * Main method
     *
     * @throws BuildException
     * @return void
     */
    public function main()
    {
        $exc = null;

        if (empty($this->tryContainer)) {
            throw new BuildException('A nested <try> element is required');
        }

        try {
            $this->tryContainer->perform();
        } catch (BuildException $e) {
            if (!empty($this->propertyName)) {
                $this->project->setProperty($this->propertyName, $e->getMessage());
            }

            if (!empty($this->referenceName)) {
                $this->project->addReference($this->referenceName, $e);
            }

            if (!empty($this->catchContainer)) {
                $this->catchContainer->perform();
            } else {
                $exc = $e;
            }
        }

        if (!empty($this->finallyContainer)) {
            $this->finallyContainer->perform();
        }

        if (!empty($exc)) {
            throw $exc;
        }
    }

    /**
     * Sets the name of the property that will
     * contain the exception message.
     *
     * @param string $property
     */
    public function setProperty($property)
    {
        $this->propertyName = (string) $property;
    }

    /**
     * Sets the name of the reference that will
     * contain the exception.
     *
     * @param Exception $reference
     *
     * @return void
     */
    public function setReference($reference)
    {
        $this->referenceName = $reference;
    }

    /**
     * Add nested <try> element
     *
     * @param SequentialTask $container
     */
    public function addTry(SequentialTask $container)
    {
        $this->tryContainer = $container;
    }

    /**
     * Add nested <catch> element
     *
     * @param SequentialTask $container
     */
    public function addCatch(SequentialTask $container)
    {
        $this->catchContainer = $container;
    }

    /**
     * Add nested <finally> element
     *
     * @param SequentialTask $container
     */
    public function addFinally(SequentialTask $container)
    {
        $this->finallyContainer = $container;
    }
}
<?php
/*
 *  $Id: e185e05826c5bf6bd0fac6652f483c67548095c6 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Sets properties to the current time, or offsets from the current time.
 * The default properties are TSTAMP, DSTAMP and TODAY;
 *
 * Based on Ant's Tstamp task.
 *
 * @author   Michiel Rook <mrook@php.net>
 * @version  $Id: e185e05826c5bf6bd0fac6652f483c67548095c6 $
 * @package  phing.tasks.system
 * @since    2.2.0
 */
class TstampTask extends Task
{
    private $customFormats = array();

    private $prefix = "";

    /**
     * Set a prefix for the properties. If the prefix does not end with a "."
     * one is automatically added.
     * @param string $prefix the prefix to use.
     */
    public function setPrefix($prefix)
    {
        $this->prefix = $prefix;

        if (!empty($this->prefix)) {
            $this->prefix .= ".";
        }
    }

    /**
     * Adds a custom format
     *
     * @param TstampCustomFormat custom format
     */
    public function addFormat(TstampCustomFormat $cf)
    {
        $this->customFormats[] = $cf;
    }

    /**
     * Create the timestamps. Custom ones are done before
     * the standard ones.
     *
     * @throws BuildException
     */
    public function main()
    {
        foreach ($this->customFormats as $cf) {
            $cf->execute($this);
        }

        $dstamp = strftime('%Y%m%d');
        $this->prefixProperty('DSTAMP', $dstamp);

        $tstamp = strftime('%H%M');
        $this->prefixProperty('TSTAMP', $tstamp);

        $today = strftime('%B %d %Y');
        $this->prefixProperty('TODAY', $today);
    }

    /**
     * helper that encapsulates prefix logic and property setting
     * policy (i.e. we use setNewProperty instead of setProperty).
     * @param $name
     * @param $value
     */
    public function prefixProperty($name, $value)
    {
        $this->getProject()->setNewProperty($this->prefix . $name, $value);
    }
}

/**
 * @package  phing.tasks.system
 */
class TstampCustomFormat
{
    private $propertyName = "";
    private $pattern = "";
    private $locale = "";
    private $timezone = "";

    /**
     * The property to receive the date/time string in the given pattern
     *
     * @param string $propertyName the name of the property.
     */
    public function setProperty($propertyName)
    {
        $this->propertyName = $propertyName;
    }

    /**
     * The date/time pattern to be used. The values are as
     * defined by the PHP strftime() function.
     *
     * @param pattern
     */
    public function setPattern($pattern)
    {
        $this->pattern = $pattern;
    }

    /**
     * The locale used to create date/time string.
     *
     * @param string $locale
     */
    public function setLocale($locale)
    {
        $this->locale = $locale;
    }

    /**
     * @param string $timezone
     */
    public function setTimezone($timezone)
    {
        $this->timezone = $timezone;
    }

    /**
     * validate parameter and execute the format.
     *
     * @param TstampTask $tstamp reference to task
     * @throws BuildException
     */
    public function execute(TstampTask $tstamp)
    {
        if (empty($this->propertyName)) {
            throw new BuildException("property attribute must be provided");
        }

        if (empty($this->pattern)) {
            throw new BuildException("pattern attribute must be provided");
        }

        $oldlocale = "";
        if (!empty($this->locale)) {
            $oldlocale = setlocale(LC_ALL, 0);
            setlocale(LC_ALL, $this->locale);
        }

        $savedTimezone = date_default_timezone_get();
        if (!empty($this->timezone)) {
            date_default_timezone_set($this->timezone);
        }

        $value = strftime($this->pattern);
        $tstamp->prefixProperty($this->propertyName, $value);

        if (!empty($this->locale)) {
            // reset locale
            setlocale(LC_ALL, $oldlocale);
        }

        if (!empty($this->timezone)) {
            date_default_timezone_set($savedTimezone);
        }
    }
}
<?php
/*
 *  $Id: f91ebca00240cc8f22e642115e0aa0da140dc4a4 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Register a datatype for use within a buildfile.
 *
 * This is for registering your own datatypes for use within a buildfile.
 *
 * If you find that you are using a particular class frequently, you may want to edit the
 * phing/types/defaults.properties file so that it is included by default.  You may also
 * want to submit it (if LGPL or compatible license) to be included in Phing distribution.
 *
 * <pre>
 *   <typedef name="mytype" classname="path.to.MyHandlingClass"/>
 *   .
 *   <sometask ...>
 *     <mytype param1="val1" param2="val2"/>
 *   </sometask>
 * </pre>
 *
 * TODO:
 *    -- possibly refactor since this is almost the same as TaskDefTask
 *      (right now these are just too simple to really justify creating an abstract class)
 *
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: f91ebca00240cc8f22e642115e0aa0da140dc4a4 $
 * @package   phing.tasks.system
 */
class TypedefTask extends Task
{

    /** Tag name for datatype that will be used in XML */
    private $name;

    /**
     * Classname of task to register.
     * This can be a dot-path -- relative to a location on PHP include_path.
     * E.g. path.to.MyClass ->  path/to/MyClass.php
     * @var string
     */
    private $classname;

    /**
     * Path to add to PHP include_path to aid in finding specified class.
     * @var Path
     */
    private $classpath;

    /** Refid to already defined classpath */
    private $classpathId;

    /**
     * Set the classpath to be used when searching for component being defined
     *
     * @param Path $classpath A Path object containing the classpath.
     */
    public function setClasspath(Path $classpath)
    {
        if ($this->classpath === null) {
            $this->classpath = $classpath;
        } else {
            $this->classpath->append($classpath);
        }
    }

    /**
     * Create the classpath to be used when searching for component being defined
     *
     * @return Path
     */
    public function createClasspath()
    {
        if ($this->classpath === null) {
            $this->classpath = new Path($this->project);
        }

        return $this->classpath->createPath();
    }

    /**
     * Reference to a classpath to use when loading the files.
     * @param Reference $r
     */
    public function setClasspathRef(Reference $r)
    {
        $this->classpathId = $r->getRefId();
        $this->createClasspath()->setRefid($r);
    }

    /** Main entry point */
    public function main()
    {
        if ($this->name === null || $this->classname === null) {
            throw new BuildException("You must specify name and class attributes for <typedef>.");
        }
        $this->project->addDataTypeDefinition($this->name, $this->classname, $this->classpath);
    }

    /**
     * Sets the name that will be used in XML buildfile.
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Sets the class name / dotpath to use.
     * @param string $class
     */
    public function setClassname($class)
    {
        $this->classname = $class;
    }
}
<?php
/*
 * $Id: bf8189887471df0dbee59309b40045fb20db6f8c $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
include_once 'phing/tasks/system/condition/Condition.php';
include_once 'phing/tasks/system/PropertyTask.php';
include_once 'phing/util/DirectoryScanner.php';
include_once 'phing/util/SourceFileScanner.php';
include_once 'phing/mappers/MergeMapper.php';

/**
 * Sets the given property if the specified target has a timestamp
 * greater than all of the source files.
 *
 * @author    Hans Lellelid <hans@xmpl.org> (Phing)
 * @author    William Ferguson <williamf@mincom.com> (Ant)
 * @author    Hiroaki Nakamura <hnakamur@mc.neweb.ne.jp> (Ant)
 * @author    Stefan Bodewig <stefan.bodewig@epost.de> (Ant)
 * @version   $Id: bf8189887471df0dbee59309b40045fb20db6f8c $
 * @package   phing.tasks.system
 */
class UpToDateTask extends Task implements Condition
{

    private $_property;
    private $_value;
    private $_sourceFile;
    private $_targetFile;
    private $sourceFileSets = array();
    private $_filelists = array();

    protected $mapperElement = null;

    /**
     * The property to set if the target file is more up-to-date than
     * (each of) the source file(s).
     *
     * @param string $property the name of the property to set if Target is up-to-date.
     */
    public function setProperty($property)
    {
        $this->_property = $property;
    }

    /**
     * Get property name
     * @return string property the name of the property to set if Target is up-to-date.
     */
    public function getProperty()
    {
        return $this->_property;
    }

    /**
     * The value to set the named property to if the target file is more
     * up-to-date than (each of) the source file(s). Defaults to 'true'.
     *
     * @param the $value
     * @internal param the $value value to set the property to if Target is up-to-date
     */
    public function setValue($value)
    {
        $this->_value = $value;
    }

    /**
     * Returns the value, or "true" if a specific value wasn't provided.
     */
    private function getValue()
    {
        return ($this->_value !== null) ? $this->_value : "true";
    }

    /**
     * The file which must be more up-to-date than (each of) the source file(s)
     * if the property is to be set.
     *
     * @param the $file
     * @internal param the $file file we are checking against.
     */
    public function setTargetFile($file)
    {
        if (is_string($file)) {
            $file = new PhingFile($file);
        }
        $this->_targetFile = $file;
    }

    /**
     * The file that must be older than the target file
     * if the property is to be set.
     *
     * @param the $file
     * @internal param the $file file we are checking against the target file.
     */
    public function setSrcfile($file)
    {
        if (is_string($file)) {
            $file = new PhingFile($file);
        }
        $this->_sourceFile = $file;
    }

    /**
     * Nested <srcfiles> element.
     *
     * @deprecated Deprecated since Phing 2.4.0
     */
    public function createSrcfiles()
    {
        $fs = new FileSet();
        $this->sourceFileSets[] = $fs;

        return $fs;
    }

    /**
     * Nested <fileset> element.
     * @param FileSet $fs
     */
    public function addFileset(FileSet $fs)
    {
        $this->sourceFileSets[] = $fs;
    }

    /**
     * Supports embedded <filelist> element.
     * @return FileList
     */
    public function createFileList()
    {
        $num = array_push($this->_filelists, new FileList());

        return $this->_filelists[$num - 1];
    }

    /**
     * Defines the FileNameMapper to use (nested mapper element).
     */
    public function createMapper()
    {
        if ($this->mapperElement !== null) {
            throw new BuildException("Cannot define more than one mapper",
                $this->location);
        }
        $this->mapperElement = new Mapper($this->getProject());

        return $this->mapperElement;
    }

    /**
     * Evaluate (all) target and source file(s) to
     * see if the target(s) is/are up-to-date.
     * @throws BuildException
     * @return boolean
     */
    public function evaluate()
    {
        if (count($this->sourceFileSets) == 0 && count($this->_filelists) == 0 && $this->_sourceFile === null) {
            throw new BuildException("At least one srcfile or a nested "
                . "<fileset> or <filelist> element must be set.");
        }

        if ((count($this->sourceFileSets) > 0 || count($this->_filelists) > 0) && $this->_sourceFile !== null) {
            throw new BuildException("Cannot specify both the srcfile "
                . "attribute and a nested <fileset> "
                . "or <filelist> element.");
        }

        if ($this->_targetFile === null && $this->mapperElement === null) {
            throw new BuildException("The targetfile attribute or a nested "
                . "mapper element must be set.");
        }

        // if the target file is not there, then it can't be up-to-date
        if ($this->_targetFile !== null && !$this->_targetFile->exists()) {
            return false;
        }

        // if the source file isn't there, throw an exception
        if ($this->_sourceFile !== null && !$this->_sourceFile->exists()) {
            throw new BuildException($this->_sourceFile->getAbsolutePath()
                . " not found.");
        }

        $upToDate = true;
        for ($i = 0, $size = count($this->sourceFileSets); $i < $size && $upToDate; $i++) {
            $fs = $this->sourceFileSets[$i];
            $ds = $fs->getDirectoryScanner($this->project);
            $upToDate = $upToDate && $this->scanDir(
                    $fs->getDir($this->project),
                    $ds->getIncludedFiles()
                );
        }

        for ($i = 0, $size = count($this->_filelists); $i < $size && $upToDate; $i++) {
            $fl = $this->_filelists[$i];
            $srcFiles = $fl->getFiles($this->project);
            $upToDate = $upToDate && $this->scanDir(
                    $fl->getDir($this->project),
                    $srcFiles
                );
        }

        if ($this->_sourceFile !== null) {
            if ($this->mapperElement === null) {
                $upToDate = $upToDate &&
                    ($this->_targetFile->lastModified() >= $this->_sourceFile->lastModified());
            } else {
                $sfs = new SourceFileScanner($this);
                $upToDate = $upToDate &&
                    count(
                        $sfs->restrict(
                            $this->_sourceFile->getAbsolutePath(),
                            null,
                            null,
                            $this->mapperElement->getImplementation()
                        )
                    ) === 0;
            }
        }

        return $upToDate;
    }


    /**
     * Sets property to true if target file(s) have a more recent timestamp
     * than (each of) the corresponding source file(s).
     * @throws BuildException
     */
    public function main()
    {
        if ($this->_property === null) {
            throw new BuildException("property attribute is required.",
                $this->location);
        }
        $upToDate = $this->evaluate();
        if ($upToDate) {
            $property = $this->project->createTask('property');
            $property->setName($this->getProperty());
            $property->setValue($this->getValue());
            $property->setOverride(true);
            $property->main(); // execute

            if ($this->mapperElement === null) {
                $this->log(
                    "File \"" . $this->_targetFile->getAbsolutePath()
                    . "\" is up-to-date.",
                    Project::MSG_VERBOSE
                );
            } else {
                $this->log(
                    "All target files are up-to-date.",
                    Project::MSG_VERBOSE
                );
            }
        }
    }

    /**
     * @param PhingFile $srcDir
     * @param $files
     * @return bool
     */
    protected function scanDir(PhingFile $srcDir, $files)
    {
        $sfs = new SourceFileScanner($this);
        $mapper = null;
        $dir = $srcDir;
        if ($this->mapperElement === null) {
            $mm = new MergeMapper();
            $mm->setTo($this->_targetFile->getAbsolutePath());
            $mapper = $mm;
            $dir = null;
        } else {
            $mapper = $this->mapperElement->getImplementation();
        }

        return (count($sfs->restrict($files, $srcDir, $dir, $mapper)) === 0);
    }
}
<?php
/*
 * $Id: 139b0653bc863b536f2a2f488238c4e702c00c71 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';
require_once 'phing/tasks/system/condition/ConditionBase.php';

/**
 *  Based on Apache Ant Wait For:
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 * @author    Michiel Rook <mrook@php.net>
 * @version   $Id: 139b0653bc863b536f2a2f488238c4e702c00c71 $
 * @package   phing.tasks.system
 */
class WaitForTask extends ConditionBase
{
    const ONE_MILLISECOND = 1;
    const ONE_SECOND = 1000;
    const ONE_MINUTE = 60000;
    const ONE_HOUR = 3600000;
    const ONE_DAY = 86400000;
    const ONE_WEEK = 604800000;

    const DEFAULT_MAX_WAIT_MILLIS = 180000;
    const DEFAULT_CHECK_MILLIS = 500;

    protected $maxWait = self::DEFAULT_MAX_WAIT_MILLIS;
    protected $maxWaitMultiplier = self::ONE_MILLISECOND;

    protected $checkEvery = self::DEFAULT_CHECK_MILLIS;
    protected $checkEveryMultiplier = self::ONE_MILLISECOND;

    protected $timeoutProperty = null;

    public function __construct($taskName = 'waitfor')
    {
        parent::__construct($taskName);
    }

    /**
     * Set the maximum length of time to wait.
     * @param int $maxWait
     */
    public function setMaxWait($maxWait)
    {
        $this->maxWait = (int) $maxWait;
    }

    /**
     * Set the max wait time unit
     * @param string $maxWaitUnit
     */
    public function setMaxWaitUnit($maxWaitUnit)
    {
        $this->maxWaitMultiplier = $this->_convertUnit($maxWaitUnit);
    }

    /**
     * Set the time between each check
     * @param int $checkEvery
     */
    public function setCheckEvery($checkEvery)
    {
        $this->checkEvery = (int) $checkEvery;
    }

    /**
     * Set the check every time unit
     * @param string $checkEveryUnit
     * @return void
     */
    public function setCheckEveryUnit($checkEveryUnit)
    {
        $this->checkEveryMultiplier = $this->_convertUnit($checkEveryUnit);
    }

    /**
     * Name of the property to set after a timeout.
     * @param string $timeoutProperty
     * @return void
     */
    public function setTimeoutProperty($timeoutProperty)
    {
        $this->timeoutProperty = $timeoutProperty;
    }

    /**
     * Convert the unit to a multipler.
     * @param string $unit
     * @throws BuildException
     * @return int
     */
    protected function _convertUnit($unit)
    {
        switch ($unit) {
            case "week":
            {
                return self::ONE_WEEK;
            }

            case "day":
            {
                return self::ONE_DAY;
            }

            case "hour":
            {
                return self::ONE_HOUR;
            }

            case "minute":
            {
                return self::ONE_MINUTE;
            }

            case "second":
            {
                return self::ONE_SECOND;
            }

            case "millisecond":
            {
                return self::ONE_MILLISECOND;
            }

            default:
                {
                throw new BuildException("Illegal unit '$unit'");
                }
        }
    }

    /**
     * Check repeatedly for the specified conditions until they become
     * true or the timeout expires.
     * @throws BuildException
     */
    public function main()
    {
        if ($this->countConditions() > 1) {
            throw new BuildException("You must not nest more than one condition into <waitfor>");
        }

        if ($this->countConditions() < 1) {
            throw new BuildException("You must nest a condition into <waitfor>");
        }

        $cs = $this->getIterator();
        $condition = $cs->current();

        $maxWaitMillis = $this->maxWait * $this->maxWaitMultiplier;
        $checkEveryMillis = $this->checkEvery * $this->checkEveryMultiplier;

        $start = microtime(true) * 1000;
        $end = $start + $maxWaitMillis;

        while (microtime(true) * 1000 < $end) {
            if ($condition->evaluate()) {
                $this->processSuccess();
                return;
            }

            usleep($checkEveryMillis * 1000);
        }

        $this->processTimeout();
    }

    protected function processSuccess()
    {
        $this->log($this->getTaskName() . ": condition was met", Project::MSG_VERBOSE);
    }

    protected function processTimeout()
    {
        $this->log($this->getTaskName() . ": timeout", Project::MSG_VERBOSE);

        if ($this->timeoutProperty != null) {
            $this->project->setNewProperty($this->timeoutProperty, "true");
        }
    }
}
<?php
/*
 *  $Id: 45ee18d3a5c2d9188f6fa98ea442be43e9dff547 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/EchoTask.php';

/**
 * Simple task to echo a warning message (Project::MSG_WARN) to all output devices.
 *
 * @author   Hans Lellelid <hans@xmpl.org>
 * @version  $Id: 45ee18d3a5c2d9188f6fa98ea442be43e9dff547 $
 * @package  phing.tasks.system
 */
class WarnTask extends EchoTask
{
    public function main()
    {
        $this->log($this->msg, Project::MSG_WARN);
    }
}
<?php
/*
 *  $Id: 705dee4ef4327cac49149cc3dfeb5a4749273f1a $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/tasks/system/CopyTask.php';
include_once 'phing/system/io/FileReader.php';
include_once 'phing/system/io/FileWriter.php';
include_once 'phing/filters/XsltFilter.php';

/**
 * Implements an XSLT processing filter while copying files.
 *
 * This is a shortcut for calling the <copy> task with the XSLTFilter used
 * in the <filterchains> section.
 *
 * @author    Andreas Aderhold, andi@binarycloud.com
 * @version   $Id: 705dee4ef4327cac49149cc3dfeb5a4749273f1a $
 * @package   phing.tasks.system
 */
class XsltTask extends CopyTask
{

    /** XSLTFilter object that we use to handle transformation. */
    private $xsltFilter;

    /** Parameters to pass to XSLT procesor. */
    private $parameters = array();

    /**
     * Setup the filterchains w/ XSLTFilter that we will use while copying the files.
     */
    public function init()
    {
        $xf = new XsltFilter();
        $chain = $this->createFilterChain($this->getProject());
        $chain->addXsltFilter($xf);
        $this->xsltFilter = $xf;
    }

    /**
     * Set any XSLT Param and invoke CopyTask::main()
     * @see CopyTask::main()
     */
    public function main()
    {
        $this->log("Doing XSLT transformation using stylesheet " . $this->xsltFilter->getStyle(), Project::MSG_VERBOSE);
        $this->xsltFilter->setParams($this->parameters);
        parent::main();
    }

    /**
     * Set the stylesheet to use.
     * @param PhingFile $style
     */
    public function setStyle(PhingFile $style)
    {
        $this->xsltFilter->setStyle($style);
    }

    /**
     * Whether to resolve entities in the XML document.
     *
     * @param bool $resolveExternals
     *
     * @since 2.4
     */
    public function setResolveDocumentExternals($resolveExternals)
    {
        $this->xsltFilter->setResolveDocumentExternals((bool) $resolveExternals);
    }

    /**
     * Whether to resolve entities in the stylesheet.
     *
     * @param bool $resolveExternals
     *
     * @since 2.4
     */
    public function setResolveStylesheetExternals($resolveExternals)
    {
        $this->xsltFilter->setResolveStylesheetExternals((bool) $resolveExternals);
    }

    /**
     * Support nested <param> tags using XSLTParam class.
     * @return XSLTParam
     */
    public function createParam()
    {
        $num = array_push($this->parameters, new XSLTParam());

        return $this->parameters[$num - 1];
    }
}
<?php
/*
 *  $Id: b120ee679285146acd231a143251a1678db95566 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/system/io/PhingFile.php';
include_once 'phing/types/DataType.php';
include_once 'phing/types/PatternSet.php';
include_once 'phing/types/selectors/BaseSelector.php';
include_once 'phing/types/selectors/SelectorContainer.php';
include_once 'phing/types/selectors/BaseSelectorContainer.php';

// Load all of the selectors (not really necessary but
// helps reveal parse errors right away)

include_once 'phing/types/selectors/AndSelector.php';
include_once 'phing/types/selectors/ContainsSelector.php';
include_once 'phing/types/selectors/ContainsRegexpSelector.php';
include_once 'phing/types/selectors/DateSelector.php';
include_once 'phing/types/selectors/DependSelector.php';
include_once 'phing/types/selectors/DepthSelector.php';
include_once 'phing/types/selectors/DifferentSelector.php';
include_once 'phing/types/selectors/ExtendSelector.php';
include_once 'phing/types/selectors/FilenameSelector.php';
include_once 'phing/types/selectors/MajoritySelector.php';
include_once 'phing/types/selectors/NoneSelector.php';
include_once 'phing/types/selectors/NotSelector.php';
include_once 'phing/types/selectors/OrSelector.php';
include_once 'phing/types/selectors/PresentSelector.php';
include_once 'phing/types/selectors/SizeSelector.php';
include_once 'phing/types/selectors/TypeSelector.php';
include_once 'phing/types/selectors/ReadableSelector.php';
include_once 'phing/types/selectors/WritableSelector.php';

include_once 'phing/util/DirectoryScanner.php';

/**
 * The FileSet class provides methods and properties for accessing
 * and managing filesets. It extends ProjectComponent and thus inherits
 * all methods and properties (not explicitly declared). See ProjectComponent
 * for further detail.
 *
 * TODO:
 *   - merge this with patternsets: FileSet extends PatternSet !!!
 *     requires additional mods to the parsing algo
 *         [HL] .... not sure if that really makes so much sense.  I think
 *            that perhaps they should use common utility class if there really
 *            is that much shared functionality
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version    $Id: b120ee679285146acd231a143251a1678db95566 $
 * @see        ProjectComponent
 * @package    phing.types
 */
class AbstractFileSet extends DataType implements SelectorContainer
{

    // These vars are public for cloning purposes

    /**
     * @var boolean
     */
    public $useDefaultExcludes = true;

    /**
     * Whether to expand/dereference symbolic links, default is false
     * @var boolean
     */
    protected $expandSymbolicLinks = false;

    /**
     * @var PatternSet
     */
    public $defaultPatterns;

    public $additionalPatterns = array();
    public $dir;
    public $isCaseSensitive = true;
    public $selectors = array();

    /**
     * @param null $fileset
     */
    public function __construct($fileset = null)
    {
        if ($fileset !== null && ($fileset instanceof FileSet)) {
            $this->dir = $fileset->dir;
            $this->defaultPatterns = $fileset->defaultPatterns;
            $this->additionalPatterns = $fileset->additionalPatterns;
            $this->useDefaultExcludes = $fileset->useDefaultExcludes;
            $this->isCaseSensitive = $fileset->isCaseSensitive;
            $this->selectors = $fileset->selectors;
        }
        $this->defaultPatterns = new PatternSet();
    }

    /**
     * Sets whether to expand/dereference symbolic links, default is false
     * @var boolean
     */
    public function setExpandSymbolicLinks($expandSymbolicLinks)
    {
        $this->expandSymbolicLinks = $expandSymbolicLinks;
    }

    /**
     * Makes this instance in effect a reference to another PatternSet
     * instance.
     * You must not set another attribute or nest elements inside
     * this element if you make it a reference.
     * @param Reference $r
     * @throws BuildException
     */
    public function setRefid(Reference $r)
    {
        if ((isset($this->dir) && !is_null($this->dir)) || $this->defaultPatterns->hasPatterns()) {
            throw $this->tooManyAttributes();
        }
        if (!empty($this->additionalPatterns)) {
            throw $this->noChildrenAllowed();
        }
        if (!empty($this->selectors)) {
            throw $this->noChildrenAllowed();
        }
        parent::setRefid($r);
    }

    /**
     * @param $dir
     * @throws BuildException
     */
    public function setDir($dir)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        if ($dir instanceof PhingFile) {
            $dir = $dir->getPath();
        }
        $this->dir = new PhingFile((string) $dir);
    }

    /**
     * @param Project $p
     * @return mixed
     * @throws BuildException
     */
    public function getDir(Project $p)
    {
        if ($this->isReference()) {
            return $this->getRef($p)->getDir($p);
        }

        return $this->dir;
    }

    /**
     * @return mixed
     * @throws BuildException
     */
    public function createPatternSet()
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }
        $num = array_push($this->additionalPatterns, new PatternSet());

        return $this->additionalPatterns[$num - 1];
    }

    /**
     * add a name entry on the include list
     */
    public function createInclude()
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }

        return $this->defaultPatterns->createInclude();
    }

    /**
     * add a name entry on the include files list
     */
    public function createIncludesFile()
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }

        return $this->defaultPatterns->createIncludesFile();
    }

    /**
     * add a name entry on the exclude list
     */
    public function createExclude()
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }

        return $this->defaultPatterns->createExclude();
    }

    /**
     * add a name entry on the include files list
     */
    public function createExcludesFile()
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }

        return $this->defaultPatterns->createExcludesFile();
    }

    public function setFile(PhingFile $file)
    {
        $this->setDir($file->getParentFile());
        $this->createInclude()->setName($file->getName());
    }

    /**
     * Sets the set of include patterns. Patterns may be separated by a comma
     * or a space.
     * @param $includes
     * @throws BuildException
     */
    public function setIncludes($includes)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        $this->defaultPatterns->setIncludes($includes);
    }

    /**
     * Sets the set of exclude patterns. Patterns may be separated by a comma
     * or a space.
     * @param $excludes
     * @throws BuildException
     */
    public function setExcludes($excludes)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        $this->defaultPatterns->setExcludes($excludes);
    }

    /**
     * Sets the name of the file containing the includes patterns.
     *
     * @param PhingFile $incl The file to fetch the include patterns from.
     * @throws BuildException
     */
    public function setIncludesfile(PhingFile $incl)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        $this->defaultPatterns->setIncludesfile($incl);
    }

    /**
     * Sets the name of the file containing the includes patterns.
     *
     * @param $excl The file to fetch the exclude patterns from.
     * @throws BuildException
     */
    public function setExcludesfile($excl)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        $this->defaultPatterns->setExcludesfile($excl);
    }

    /**
     * Sets whether default exclusions should be used or not.
     *
     * @param $useDefaultExcludes "true"|"on"|"yes" when default exclusions
     *                           should be used, "false"|"off"|"no" when they
     *                           shouldn't be used.
     * @throws BuildException
     * @return void
     */
    public function setDefaultexcludes($useDefaultExcludes)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        $this->useDefaultExcludes = $useDefaultExcludes;
    }

    /**
     * Sets case sensitivity of the file system
     * @param $isCaseSensitive
     */
    public function setCaseSensitive($isCaseSensitive)
    {
        $this->isCaseSensitive = $isCaseSensitive;
    }

    /** returns a reference to the dirscanner object belonging to this fileset
     * @param Project $p
     * @throws BuildException
     * @throws Exception
     * @return \DirectoryScanner
     */
    public function getDirectoryScanner(Project $p)
    {
        if ($this->isReference()) {
            $o = $this->getRef($p);

            return $o->getDirectoryScanner($p);
        }

        if ($this->dir === null) {
            throw new BuildException("No directory specified for fileset.");
        }
        if (!$this->dir->exists()) {
            throw new BuildException("Directory " . $this->dir->getAbsolutePath() . " not found.");
        }
        if (!$this->dir->isLink() || !$this->expandSymbolicLinks) {
            if (!$this->dir->isDirectory()) {
                throw new BuildException($this->dir->getAbsolutePath() . " is not a directory.");
            }
        }
        $ds = new DirectoryScanner();
        $ds->setExpandSymbolicLinks($this->expandSymbolicLinks);
        $this->setupDirectoryScanner($ds, $p);
        $ds->scan();

        return $ds;
    }

    /** feed dirscanner with infos defined by this fileset
     * @param DirectoryScanner $ds
     * @param Project $p
     * @throws BuildException
     * @throws Exception
     */
    protected function setupDirectoryScanner(DirectoryScanner $ds, Project $p)
    {
        if ($ds === null) {
            throw new Exception("DirectoryScanner cannot be null");
        }
        // FIXME - pass dir directly when dirscanner supports File
        $ds->setBasedir($this->dir->getPath());

        foreach ($this->additionalPatterns as $addPattern) {
            $this->defaultPatterns->append($addPattern, $p);
        }

        $ds->setIncludes($this->defaultPatterns->getIncludePatterns($p));
        $ds->setExcludes($this->defaultPatterns->getExcludePatterns($p));

        $p->log(
            "FileSet: Setup file scanner in dir " . $this->dir->__toString(
            ) . " with " . $this->defaultPatterns->toString(),
            Project::MSG_DEBUG
        );

        if ($ds instanceof SelectorScanner) {
            $ds->setSelectors($this->getSelectors($p));
        }

        if ($this->useDefaultExcludes) {
            $ds->addDefaultExcludes();
        }
        $ds->setCaseSensitive($this->isCaseSensitive);
    }


    /**
     * Performs the check for circular references and returns the
     * referenced FileSet.
     *
     * @param Project $p
     *
     * @throws BuildException
     *
     * @return FileSet
     */
    public function getRef(Project $p)
    {
        if (!$this->checked) {
            $stk = array();
            array_push($stk, $this);
            $this->dieOnCircularReference($stk, $p);
        }

        $o = $this->ref->getReferencedObject($p);
        if (!($o instanceof FileSet)) {
            $msg = $this->ref->getRefId() . " doesn't denote a fileset";
            throw new BuildException($msg);
        } else {
            return $o;
        }
    }

    // SelectorContainer methods

    /**
     * Indicates whether there are any selectors here.
     *
     * @return boolean Whether any selectors are in this container
     */
    public function hasSelectors()
    {
        if ($this->isReference() && $this->getProject() !== null) {
            return $this->getRef($this->getProject())->hasSelectors();
        }

        return !empty($this->selectors);
    }

    /**
     * Indicates whether there are any patterns here.
     *
     * @return boolean Whether any patterns are in this container.
     */
    public function hasPatterns()
    {

        if ($this->isReference() && $this->getProject() !== null) {
            return $this->getRef($this->getProject())->hasPatterns();
        }

        if ($this->defaultPatterns->hasPatterns($this->getProject())) {
            return true;
        }

        for ($i = 0, $size = count($this->additionalPatterns); $i < $size; $i++) {
            $ps = $this->additionalPatterns[$i];
            if ($ps->hasPatterns($this->getProject())) {
                return true;
            }
        }

        return false;
    }

    /**
     * Gives the count of the number of selectors in this container
     *
     * @throws Exception
     * @return int The number of selectors in this container
     */
    public function selectorCount()
    {
        if ($this->isReference() && $this->getProject() !== null) {
            try {
                return $this->getRef($this->getProject())->selectorCount();
            } catch (Exception $e) {
                throw $e;
            }
        }

        return count($this->selectors);
    }

    /**
     * Returns the set of selectors as an array.
     *
     * @param Project $p
     * @throws BuildException
     * @return array of selectors in this container
     */
    public function getSelectors(Project $p)
    {
        if ($this->isReference()) {
            return $this->getRef($p)->getSelectors($p);
        } else {
            // *copy* selectors
            $result = array();
            for ($i = 0, $size = count($this->selectors); $i < $size; $i++) {
                $result[] = clone $this->selectors[$i];
            }

            return $result;
        }
    }

    /**
     * Returns an array for accessing the set of selectors.
     *
     * @return array The array of selectors
     */
    public function selectorElements()
    {
        if ($this->isReference() && $this->getProject() !== null) {
            return $this->getRef($this->getProject())->selectorElements();
        }

        return $this->selectors;
    }

    /**
     * Add a new selector into this container.
     *
     * @param FileSelector $selector new selector to add
     *
     * @throws BuildException
     *
     * @return void
     */
    public function appendSelector(FileSelector $selector)
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }
        $this->selectors[] = $selector;
    }

    /* Methods below all add specific selectors */

    /**
     * add a "Select" selector entry on the selector list
     *
     * @return SelectSelector
     */
    public function createSelector()
    {
        $o = new SelectSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add an "And" selector entry on the selector list
     *
     * @return AndSelector
     */
    public function createAnd()
    {
        $o = new AndSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add an "Or" selector entry on the selector list
     *
     * @return OrSelector
     */
    public function createOr()
    {
        $o = new OrSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a "Not" selector entry on the selector list
     */
    public function createNot()
    {
        $o = new NotSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a "None" selector entry on the selector list
     */
    public function createNone()
    {
        $o = new NoneSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a majority selector entry on the selector list
     */
    public function createMajority()
    {
        $o = new MajoritySelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a selector date entry on the selector list
     */
    public function createDate()
    {
        $o = new DateSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a selector size entry on the selector list
     */
    public function createSize()
    {
        $o = new SizeSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a selector filename entry on the selector list
     */
    public function createFilename()
    {
        $o = new FilenameSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add an extended selector entry on the selector list
     */
    public function createCustom()
    {
        $o = new ExtendSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a contains selector entry on the selector list
     */
    public function createContains()
    {
        $o = new ContainsSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a contains selector entry on the selector list
     */
    public function createContainsRegexp()
    {
        $o = new ContainsRegexpSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a present selector entry on the selector list
     */
    public function createPresent()
    {
        $o = new PresentSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a depth selector entry on the selector list
     */
    public function createDepth()
    {
        $o = new DepthSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a depends selector entry on the selector list
     */
    public function createDepend()
    {
        $o = new DependSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a type selector entry on the selector list
     */
    public function createType()
    {
        $o = new TypeSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a readable selector entry on the selector list
     */
    public function createReadable()
    {
        $o = new ReadableSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a writable selector entry on the selector list
     */
    public function createWritable()
    {
        $o = new WritableSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a different selector entry on the selector list
     */
    public function createDifferent()
    {
        $o = new DifferentSelector();
        $this->appendSelector($o);

        return $o;
    }
}
<?php
/*
 *  $Id: c94c75e28f62cc0576c82b22bc8c46615032be48 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Commandline objects help handling command lines specifying processes to
 * execute.
 *
 * The class can be used to define a command line as nested elements or as a
 * helper to define a command line by an application.
 * <p>
 * <code>
 * &lt;someelement&gt;<br>
 * &nbsp;&nbsp;&lt;acommandline executable="/executable/to/run"&gt;<br>
 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 1" /&gt;<br>
 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument line="argument_1 argument_2 argument_3" /&gt;<br>
 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 4" /&gt;<br>
 * &nbsp;&nbsp;&lt;/acommandline&gt;<br>
 * &lt;/someelement&gt;<br>
 * </code>
 * The element <code>someelement</code> must provide a method
 * <code>createAcommandline</code> which returns an instance of this class.
 *
 * @author thomas.haas@softwired-inc.com
 * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
 * @package phing.types
 */
class Commandline
{

    /**
     * @var CommandlineArgument[]
     */
    public $arguments = array(); // public so "inner" class can access

    /**
     * Full path (if not on %PATH% env var) to executable program.
     * @var string
     */
    public $executable; // public so "inner" class can access

    const DISCLAIMER = "The ' characters around the executable and arguments are not part of the command.";

    /**
     * @param null $to_process
     * @throws BuildException
     */
    public function __construct($to_process = null)
    {
        if ($to_process !== null) {
            $tmp = $this->translateCommandline($to_process);
            if ($tmp) {
                $this->setExecutable(array_shift($tmp)); // removes first el
                foreach ($tmp as $arg) { // iterate through remaining elements
                    $this->createArgument()->setValue($arg);
                }
            }
        }
    }

    /**
     * Creates an argument object and adds it to our list of args.
     *
     * <p>Each commandline object has at most one instance of the
     * argument class.</p>
     *
     * @param  boolean             $insertAtStart if true, the argument is inserted at the
     *                                            beginning of the list of args, otherwise it is appended.
     * @return CommandlineArgument
     */
    public function createArgument($insertAtStart = false)
    {
        $argument = new CommandlineArgument($this);
        if ($insertAtStart) {
            array_unshift($this->arguments, $argument);
        } else {
            array_push($this->arguments, $argument);
        }

        return $argument;
    }

    /**
     * Sets the executable to run.
     * @param $executable
     */
    public function setExecutable($executable)
    {
        if (!$executable) {
            return;
        }
        $this->executable = $executable;
        $this->executable = strtr($this->executable, '/', DIRECTORY_SEPARATOR);
        $this->executable = strtr($this->executable, '\\', DIRECTORY_SEPARATOR);
    }

    /**
     * @return string
     */
    public function getExecutable()
    {
        return $this->executable;
    }

    /**
     * @param array $arguments
     */
    public function addArguments(array $arguments)
    {
        foreach ($arguments as $arg) {
            $this->createArgument()->setValue($arg);
        }
    }

    /**
     * Returns the executable and all defined arguments.
     * @return array
     */
    public function getCommandline()
    {
        $args = $this->getArguments();
        if ($this->executable === null) {
            return $args;
        }

        return array_merge(array($this->executable), $args);
    }

    /**
     * Returns all arguments defined by <code>addLine</code>,
     * <code>addValue</code> or the argument object.
     */
    public function getArguments()
    {
        $result = array();
        foreach ($this->arguments as $arg) {
            $parts = $arg->getParts();
            if ($parts !== null) {
                foreach ($parts as $part) {
                    $result[] = $arg->escape ? self::quoteArgument($part, true) : $part;
                }
            }
        }

        return $result;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return self::toString($this->getCommandline());
    }

    /**
     * Put quotes around the given String if necessary.
     *
     * <p>If the argument doesn't include spaces or quotes, return it
     * as is. If it contains double quotes, use single quotes - else
     * surround the argument by double quotes.</p>
     *
     * @exception BuildException if the argument contains both, single
     *                           and double quotes.
     * @param $argument
     * @param bool $escape
     * @throws BuildException
     * @return string
     */
    public static function quoteArgument($argument, $escape = false)
    {
        if ($escape) {
            return escapeshellarg($argument);
        } elseif (strpos($argument, "\"") !== false && $argument != '""') {
            if (strpos($argument, "'") !== false) {
                throw new BuildException("Can't handle single and double quotes in same argument");
            } else {
                return escapeshellarg($argument);
            }
        } elseif (strpos($argument, "'") !== false || strpos($argument, " ") !== false) {
            return escapeshellarg($argument);
            //return '\"' . $argument . '\"';
        } else {
            return $argument;
        }
    }

    /**
     * Quotes the parts of the given array in way that makes them
     * usable as command line arguments.
     * @param $lines
     * @param bool $escape
     * @throws BuildException
     * @return string
     */
    public static function toString($lines, $escape = false)
    {
        // empty path return empty string
        if (!$lines) {
            return "";
        }

        // path containing one or more elements
        $result = "";
        for ($i = 0, $len = count($lines); $i < $len; $i++) {
            if ($i > 0) {
                $result .= ' ';
            }
            $result .= self::quoteArgument($lines[$i], $escape);
        }

        return $result;
    }

    /**
     *
     * @param  string $to_process
     * @throws BuildException
     * @return array
     */
    public static function translateCommandline($to_process)
    {

        if (!$to_process) {
            return array();
        }

        // parse with a simple finite state machine

        $normal = 0;
        $inQuote = 1;
        $inDoubleQuote = 2;

        $state = $normal;
        $args = array();
        $current = "";
        $lastTokenHasBeenQuoted = false;

        $tok = strtok($to_process, "");
        $tokens = preg_split('/(["\' ])/', $to_process, -1, PREG_SPLIT_DELIM_CAPTURE);
        while (($nextTok = array_shift($tokens)) !== null) {
            switch ($state) {
                case $inQuote:
                    if ("'" == $nextTok) {
                        $lastTokenHasBeenQuoted = true;
                        $state = $normal;
                    } else {
                        $current .= $nextTok;
                    }
                    break;
                case $inDoubleQuote:
                    if ("\"" == $nextTok) {
                        $lastTokenHasBeenQuoted = true;
                        $state = $normal;
                    } else {
                        $current .= $nextTok;
                    }
                    break;
                default:
                    if ("'" == $nextTok) {
                        $state = $inQuote;
                    } elseif ("\"" == $nextTok) {
                        $state = $inDoubleQuote;
                    } elseif (" " == $nextTok) {
                        if ($lastTokenHasBeenQuoted || strlen($current) != 0) {
                            $args[] = $current;
                            $current = "";
                        }
                    } else {
                        $current .= $nextTok;
                    }
                    $lastTokenHasBeenQuoted = false;
                    break;
            }
        }

        if ($lastTokenHasBeenQuoted || strlen($current) != 0) {
            $args[] = $current;
        }

        if ($state == $inQuote || $state == $inDoubleQuote) {
            throw new BuildException("unbalanced quotes in " . $to_process);
        }

        return $args;
    }

    /**
     * @return int Number of components in current commandline.
     */
    public function size()
    {
        return count($this->getCommandline());
    }

    /**
     * @return Commandline
     */
    public function __copy()
    {
        $c = new Commandline();
        $c->setExecutable($this->executable);
        $c->addArguments($this->getArguments());

        return $c;
    }

    /**
     * Return a marker.
     *
     * <p>This marker can be used to locate a position on the
     * commandline - to insert something for example - when all
     * parameters have been set.</p>
     * @return CommandlineMarker
     */
    public function createMarker()
    {
        return new CommandlineMarker($this, count($this->arguments));
    }

    /**
     * Returns a String that describes the command and arguments
     * suitable for verbose output before a call to
     * <code>Runtime.exec(String[])</code>.
     *
     * <p>This method assumes that the first entry in the array is the
     * executable to run.</p>
     * @param  array  $args CommandlineArgument[] to use
     * @return string
     */
    public function describeCommand($args = null)
    {

        if ($args === null) {
            $args = $this->getCommandline();
        }

        if (!$args) {
            return "";
        }

        $buf = "Executing '";
        $buf .= $args[0];
        $buf .= "'";
        if (count($args) > 0) {
            $buf .= " with ";
            $buf .= $this->describeArguments($args, 1);
        } else {
            $buf .= self::DISCLAIMER;
        }

        return $buf;
    }

    /**
     * Returns a String that describes the arguments suitable for
     * verbose output before a call to
     * <code>Runtime.exec(String[])</code>
     * @param array $args arguments to use (default is to use current class args)
     * @param int $offset ignore entries before this index
     * @return string
     */
    protected function describeArguments(array $args = null, $offset = 0)
    {
        if ($args === null) {
            $args = $this->getArguments();
        }

        if ($args === null || count($args) <= $offset) {
            return "";
        }

        $buf = "argument";
        if (count($args) > $offset) {
            $buf .= "s";
        }
        $buf .= ":" . PHP_EOL;
        for ($i = $offset, $alen = count($args); $i < $alen; $i++) {
            $buf .= "'" . $args[$i] . "'" . PHP_EOL;
        }
        $buf .= self::DISCLAIMER;

        return $buf;
    }
}

/**
 * "Inner" class used for nested xml command line definitions.
 *
 * @package phing.types
 */
class CommandlineArgument
{

    private $parts = array();
    private $outer;
    public $escape = false;

    /**
     * @param Commandline $outer
     */
    public function __construct(Commandline $outer)
    {
        $this->outer = $outer;
    }

    /**
     * @param bool $escape
     */
    public function setEscape($escape)
    {
        $this->escape = $escape;
    }

    /**
     * Sets a single commandline argument.
     *
     * @param string $value a single commandline argument.
     */
    public function setValue($value)
    {
        $this->parts = array($value);
    }

    /**
     * Line to split into several commandline arguments.
     *
     * @param string $line line to split into several commandline arguments
     */
    public function setLine($line)
    {
        if ($line === null) {
            return;
        }
        $this->parts = $this->outer->translateCommandline($line);
    }

    /**
     * Sets a single commandline argument and treats it like a
     * PATH - ensures the right separator for the local platform
     * is used.
     *
     * @param a $value
     * @internal param a $value single commandline argument.
     */
    public function setPath($value)
    {
        $this->parts = array((string) $value);
    }

    /**
     * Sets a single commandline argument to the absolute filename
     * of the given file.
     *
     * @param a|PhingFile $value
     * @internal param a $value single commandline argument.
     */
    public function setFile(PhingFile $value)
    {
        $this->parts = array($value->getAbsolutePath());
    }

    /**
     * Returns the parts this Argument consists of.
     * @return array string[]
     */
    public function getParts()
    {
        return $this->parts;
    }
}

/**
 * Class to keep track of the position of an Argument.
 *
 * <p>This class is there to support the srcfile and targetfile
 * elements of &lt;execon&gt; and &lt;transform&gt; - don't know
 * whether there might be additional use cases.</p> --SB
 *
 * @package phing.types
 */
class CommandlineMarker
{

    private $position;
    private $realPos = -1;
    private $outer;

    /**
     * @param Commandline $outer
     * @param $position
     */
    public function __construct(Commandline $outer, $position)
    {
        $this->outer = $outer;
        $this->position = $position;
    }

    /**
     * Return the number of arguments that preceded this marker.
     *
     * <p>The name of the executable - if set - is counted as the
     * very first argument.</p>
     */
    public function getPosition()
    {
        if ($this->realPos == -1) {
            $this->realPos = ($this->outer->executable === null ? 0 : 1);
            for ($i = 0; $i < $this->position; $i++) {
                $arg = $this->outer->arguments[$i];
                $this->realPos += count($arg->getParts());
            }
        }

        return $this->realPos;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/ProjectComponent.php';
include_once 'phing/BuildException.php';

/**
 * Base class for those classes that can appear inside the build file
 * as stand alone data types.
 *
 * This class handles the common description attribute and provides
 * a default implementation for reference handling and checking for
 * circular references that is appropriate for types that can not be
 * nested inside elements of the same type (i.e. patternset but not path)
 *
 * {@inheritdoc}
 *
 * @package   phing.types
 */
class DataType extends ProjectComponent
{
    /**
     * The descriptin the user has set.
     *
     * @var string $description
     */
    public $description = null;

    /**
     * Value to the refid attribute.
     *
     * @var Reference $ref
     */
    public $ref = null;

    /**
     * Are we sure we don't hold circular references?
     *
     * Subclasses are responsible for setting this value to false
     * if we'd need to investigate this condition (usually because a
     * child element has been added that is a subclass of DataType).
     *
     * @var boolean
     */
    protected $checked = true;

    /**
     * Sets a description of the current data type. It will be useful
     * in commenting what we are doing.
     *
     * @param string $desc
     *
     * @return void
     */
    public function setDescription($desc)
    {
        $this->description = (string) $desc;
    }

    /**
     * Return the description for the current data type.
     *
     * @retujrn string
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Has the refid attribute of this element been set?
     *
     * @return bool
     */
    public function isReference()
    {
        return ($this->ref !== null);
    }

    /**
     * Set the value of the refid attribute.
     *
     * Subclasses may need to check whether any other attributes
     * have been set as well or child elements have been created and
     * thus override this method. if they do they must call parent::setRefid()
     *
     * @param  Reference $r
     *
     * @return void
     */
    public function setRefid(Reference $r)
    {
        $this->ref = $r;
        $this->checked = false;
    }

    /**
     * Check to see whether any DataType we hold references to is
     * included in the Stack (which holds all DataType instances that
     * directly or indirectly reference this instance, including this
     * instance itself).
     *
     * If one is included, throw a BuildException created by circularReference
     *
     * This implementation is appropriate only for a DataType that
     * cannot hold other DataTypes as children.
     *
     * The general contract of this method is that it shouldn't do
     * anything if checked is true and set it to true on exit.
     *
     * @param $stk
     * @param Project $p
     *
     * @return void
     *
     * @throws BuildException
     */
    public function dieOnCircularReference(&$stk, Project $p)
    {
        if ($this->checked || !$this->isReference()) {
            return;
        }

        $o = $this->ref->getReferencedObject($p);

        if ($o instanceof DataType) {

            // TESTME - make sure that in_array() works just as well here
            //
            // check if reference is in stack
            //$contains = false;
            //for ($i=0, $size=count($stk); $i < $size; $i++) {
            //    if ($stk[$i] === $o) {
            //        $contains = true;
            //        break;
            //    }
            //}

            if (in_array($o, $stk, true)) {
                // throw build exception
                throw $this->circularReference();
            } else {
                array_push($stk, $o);
                $o->dieOnCircularReference($stk, $p);
                array_pop($stk);
            }
        }
        $this->checked = true;
    }

    public static function pushAndInvokeCircularReferenceCheck(DataType $dt, &$stk, Project $p)
    {
        array_push($stk, $dt);
        $dt->dieOnCircularReference($stk, $p);
        array_pop($stk);
    }

    /**
     * Performs the check for circular references and returns the referenced object.
     *
     * @param $requiredClass
     * @param $dataTypeName
     *
     * @throws BuildException
     *
     * @return mixed
     */
    public function getCheckedRef($requiredClass, $dataTypeName)
    {
        if (!$this->checked) {
            // should be in stack
            $stk = array();
            $stk[] = $this;
            $this->dieOnCircularReference($stk, $this->getProject());
        }

        $o = $this->ref->getReferencedObject($this->getProject());
        if (!($o instanceof $requiredClass)) {
            throw new BuildException($this->ref->getRefId() . " doesn't denote a " . $dataTypeName);
        } else {
            return $o;
        }
    }

    /**
     * Creates an exception that indicates that refid has to be the
     * only attribute if it is set.
     *
     * @return BuildException
     */
    public function tooManyAttributes()
    {
        return new BuildException("You must not specify more than one attribute when using refid");
    }

    /**
     * Creates an exception that indicates that this XML element must
     * not have child elements if the refid attribute is set.
     *
     * @return BuildException
     */
    public function noChildrenAllowed()
    {
        return new BuildException("You must not specify nested elements when using refid");
    }

    /**
     * Creates an exception that indicates the user has generated a
     * loop of data types referencing each other.
     *
     * @return BuildException
     */
    public function circularReference()
    {
        return new BuildException("This data type contains a circular reference.");
    }

    /**
     * Template method being called when the data type has been
     * parsed completely.
     *
     * {@inheritdoc}
     *
     * @return void
     */
    public function parsingComplete()
    {
    }
}
<?php

/*
 *  $Id: c7f9e63cc074434b1fe6c953adad7bf2b55e8c25 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Description is used to provide a project-wide description element
 * (that is, a description that applies to a buildfile as a whole).
 * If present, the &lt;description&gt; element is printed out before the
 * target descriptions.
 *
 * Description has no attributes, only text.  There can only be one
 * project description per project.  A second description element will
 * overwrite the first.
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Craeg Strong <cstrong@arielpartners.com> (Ant)
 * @package phing.types
 */
class Description extends DataType
{

    /**
     * Adds descriptive text to the project.
     *
     * @param $text
     * @return void
     */
    public function addText($text)
    {
        $currentDescription = $this->project->getDescription();
        if ($currentDescription === null) {
            $this->project->setDescription($text);
        } else {
            $this->project->setDescription($currentDescription . $text);
        }
    }

}
<?php

/*
 * $Id: 61cd2b26a7d3430fb6b1a2dbedd959e832440686 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/types/AbstractFileSet.php';

/**
 * Subclass as hint for supporting tasks that the included directories
 * instead of files should be used.
 *
 * @package phing.types
 */
class DirSet extends AbstractFileSet
{

    /**
     * @param null $dirset
     */
    public function __construct($dirset = null)
    {
        parent::__construct($dirset);
    }

    /**
     * Return a DirSet that has the same basedir and same patternsets
     * as this one.
     */
    public function __clone()
    {
        if ($this->isReference()) {
            return new DirSet($this->getRef($this->getProject()));
        } else {
            return new DirSet($this);
        }
    }

}
<?php
/**
 *  $Id: 0a585286fac8dd08f0fcddab3dd7a23f2e3a0776 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/types/DataType.php';
include_once 'phing/types/FileSet.php';
require_once 'phing/types/ExcludesNameEntry.php';

/**
 * Datatype which handles excluded files, classes and methods.
 *
 * @package phing.types
 * @author  Benjamin Schultz <bschultz@proqrent.de>
 * @version $Id: 0a585286fac8dd08f0fcddab3dd7a23f2e3a0776 $
 * @since   2.4.6
 */
class Excludes extends DataType
{
    /**
     * The directory scanner for getting the excluded files
     *
     * @var DirectoryScanner
     */
    private $directoryScanner = null;

    /**
     * Holds the excluded file patterns
     *
     * @var ExcludesNameEntry[]
     */
    private $files = array();

    /**
     * Holds the excluded classes
     *
     * @var ExcludesNameEntry[]
     */
    private $classes = array();

    /**
     * Holds the excluded methods
     *
     * @var ExcludesNameEntry[]
     */
    private $methods = array();

    /**
     * @param Project $project
     */
    public function __construct(Project $project)
    {
        $this->directoryScanner = new DirectoryScanner();
        $this->directoryScanner->setBasedir($project->getBasedir());
    }

    /**
     * Add a name entry on the exclude file list
     *
     * @return ExcludesNameEntry Reference to object
     */
    public function createFile()
    {
        return $this->addExcludesNameEntry($this->files);
    }

    /**
     * Add a name entry on the exclude class list
     *
     * @return ExcludesNameEntry Reference to object
     */
    public function createClass()
    {
        return $this->addExcludesNameEntry($this->classes);
    }

    /**
     * Add a name entry on the exclude method list
     *
     * @return ExcludesNameEntry Reference to object
     */
    public function createMethod()
    {
        return $this->addExcludesNameEntry($this->methods);
    }

    /**
     * Adds a new ExcludesNameEntry to the given exclusion list.
     * @param ExcludesNameEntry[] $excludesNameEntryList
     *
     * @return ExcludesNameEntry Reference to the created ExcludesNameEntry instance
     */
    private function addExcludesNameEntry(&$excludesNameEntryList)
    {
        $excludesNameEntry = new ExcludesNameEntry();
        $excludesNameEntryList[] = $excludesNameEntry;

        return $excludesNameEntry;
    }

    /**
     * Returns the excluded files
     *
     * @return array
     */
    public function getExcludedFiles()
    {
        $includes = array();

        foreach ($this->files as $file) {
            $includes[] = $file->getName();
        }

        $this->directoryScanner->setIncludes($includes);
        $this->directoryScanner->scan();

        $files = $this->directoryScanner->getIncludedFiles();
        $dir = $this->directoryScanner->getBasedir();
        $fileList = array();

        foreach ($files as $file) {
            $fileList[] = $dir . DIRECTORY_SEPARATOR . $file;
        }

        return $fileList;
    }

    /**
     * Returns the excluded class names
     *
     * @return array
     */
    public function getExcludedClasses()
    {
        $excludedClasses = array();

        foreach ($this->classes as $excludedClass) {
            $excludedClasses[] = $excludedClass->getName();
        }

        return $excludedClasses;
    }

    /**
     * Returns the excluded method names
     *
     * @return array
     */
    public function getExcludedMethods()
    {
        $excludedMethods = array();

        foreach ($this->methods as $excludedMethod) {
            $classAndMethod = explode('::', $excludedMethod->getName());
            $className = $classAndMethod[0];
            $methodName = $classAndMethod[1];

            $excludedMethods[$className][] = $methodName;
        }

        return $excludedMethods;
    }
}
<?php
/**
 *  $Id: 63ab78ae0c05fe95b19878434bce454ce8d82a97 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Class for holding nested excludes elements (file, class, method).
 *
 * @package phing.types
 * @author  Benjamin Schultz <bschultz@proqrent.de>
 * @version $Id: 63ab78ae0c05fe95b19878434bce454ce8d82a97 $
 * @since   2.4.6
 */
class ExcludesNameEntry
{
    /**
     * Holds the name of a file, class or method or a file pattern
     *
     * @var string
     */
    private $name;

    /**
     * An alias for the setName() method.
     * Set the name of a file pattern.
     *
     * @see setName()
     *
     * @param string $pattern The file pattern
     */
    public function addText($pattern)
    {
        $this->setName($pattern);
    }

    /**
     * Set the name of a file, class or method
     *
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = (string) $name;
    }

    /**
     * Get the name of a file, class or method or the file pattern
     *
     * @return string The name of a file, class or method or the file pattern
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Gets a string representation of this name or pattern.
     *
     * @return string
     */
    public function toString()
    {
        return $this->name;
    }
}
<?php
/*
 *  $Id: 3f90e744c1e410a8b6fb2cd3868b26b3c46bac9f $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/DataType.php';
include_once 'phing/system/io/PhingFile.php';

/**
 * FileList represents an explicitly named list of files. FileLists
 * are useful when you want to capture a list of files regardless of
 * whether they currently exist.
 *
 * <filelist
 *    id="docfiles"
 *   dir="${phing.docs.dir}"
 *   files="chapters/Installation.html,chapters/Setup.html"/>
 *
 * OR
 *
 * <filelist
 *         dir="${doc.src.dir}"
 *         listfile="${phing.docs.dir}/PhingGuide.book"/>
 *
 * (or a mixture of files="" and listfile="" can be used)
 *
 * @author Hans Lellelid <hans@xmpl.org>
 * @version $Id: 3f90e744c1e410a8b6fb2cd3868b26b3c46bac9f $
 * @package phing.types
 */
class FileList extends DataType
{

    // public for "cloning" purposes

    /** Array containing all filenames. */
    public $filenames = array();

    /** Base directory for this file list. */
    public $dir;

    /** @var PhingFile that contains a list of files (one per line). */
    public $listfile;

    /**
     * Construct a new FileList.
     * @param FileList $filelist
     */
    public function __construct($filelist = null)
    {
        if ($filelist !== null) {
            $this->dir = $filelist->dir;
            $this->filenames = $filelist->filenames;
            $this->listfile = $filelist->listfile;
        }
    }

    /**
     * Makes this instance in effect a reference to another FileList
     * instance.
     * @param Reference $r
     * @throws BuildException
     */
    public function setRefid(Reference $r)
    {
        if ($this->dir !== null || count($this->filenames) !== 0) {
            throw $this->tooManyAttributes();
        }
        parent::setRefid($r);
    }

    /**
     * Base directory for files in list.
     * @param PhingFile $dir
     * @throws BuildException
     */
    public function setDir(PhingFile $dir)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        if (!($dir instanceof PhingFile)) {
            $dir = new PhingFile($dir);
        }
        $this->dir = $dir;
    }

    /**
     * Get the basedir for files in list.
     * @param Project $p
     * @throws BuildException
     * @return PhingFile
     */
    public function getDir(Project $p)
    {
        if ($this->isReference()) {
            $ref = $this->getRef($p);

            return $ref->getDir($p);
        }

        return $this->dir;
    }

    /**
     * Set the array of files in list.
     * @param array $filenames
     * @throws BuildException
     */
    public function setFiles($filenames)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        if (!empty($filenames)) {
            $tok = strtok($filenames, ", \t\n\r");
            while ($tok !== false) {
                $fname = trim($tok);
                if ($fname !== "") {
                    $this->filenames[] = $tok;
                }
                $tok = strtok(", \t\n\r");
            }
        }
    }

    /**
     * Sets a source "list" file that contains filenames to add -- one per line.
     * @param string $file
     * @throws BuildException
     */
    public function setListFile($file)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        if (!($file instanceof PhingFile)) {
            $file = new PhingFile($file);
        }
        $this->listfile = $file;
    }

    /**
     * Get the source "list" file that contains file names.
     * @param  Project   $p
     * @return PhingFile
     */
    public function getListFile(Project $p)
    {
        if ($this->isReference()) {
            $ref = $this->getRef($p);

            return $ref->getListFile($p);
        }

        return $this->listfile;
    }

    /**
     * Returns the list of files represented by this FileList.
     * @param  Project $p
     * @return array
     */
    public function getFiles(Project $p)
    {

        if ($this->isReference()) {
            $ret = $this->getRef($p);
            $ret = $ret->getFiles($p);

            return $ret;
        }

        if ($this->listfile !== null) {
            $this->readListFile($p);
        }

        return $this->filenames;
    }

    /**
     * Performs the check for circular references and returns the
     * referenced FileSet.
     * @param Project $p
     *
     * @throws BuildException
     *
     * @return FileList
     */
    public function getRef(Project $p)
    {
        if (!$this->checked) {
            $stk = array();
            array_push($stk, $this);
            $this->dieOnCircularReference($stk, $p);
        }

        $o = $this->ref->getReferencedObject($p);
        if (!($o instanceof FileList)) {
            throw new BuildException($this->ref->getRefId() . " doesn't denote a filelist");
        } else {
            return $o;
        }
    }

    /**
     * Reads file names from a file and adds them to the files array.
     *
     * @param Project $p
     *
     * @throws BuildException
     */
    private function readListFile(Project $p)
    {
        $listReader = null;
        try {
            // Get a FileReader
            $listReader = new BufferedReader(new FileReader($this->listfile));

            $line = $listReader->readLine();
            while ($line !== null) {
                if (!empty($line)) {
                    $line = $p->replaceProperties($line);
                    $this->filenames[] = trim($line);
                }
                $line = $listReader->readLine();
            }
        } catch (Exception $e) {
            if ($listReader) {
                $listReader->close();
            }
            throw new BuildException("An error occurred while reading from list file " . $this->listfile->__toString(
                ) . ": " . $e->getMessage());
        }

        $listReader->close();
    }
}
<?php
/**
 * $Id: 12bd2848ee5d90a3b9a3c0e3e5d832bece06deb8 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/AbstractFileSet.php';

/**
 * Moved out of MatchingTask to make it a standalone object that could
 * be referenced (by scripts for example).
 *
 * @package phing.types
 * @author  Hans Lellelid <hans@xmpl.org> (Phing)
 * @author  Arnout J. Kuiper <ajkuiper@wxs.nl> (Ant)
 * @author  Stefano Mazzocchi <stefano@apache.org> (Ant)
 * @author  Sam Ruby <rubys@us.ibm.com> (Ant)
 * @author  Jon S. Stevens <jon@clearink.com> (Ant)
 * @author  Stefan Bodewig <stefan.bodewig@epost.de> (Ant)
 * @author  Magesh Umasankar (Ant)
 */
class FileSet extends AbstractFileSet
{
    /**
     * Return a FileSet that has the same basedir and same patternsets as this one.
     */
    public function __clone()
    {
        if ($this->isReference()) {
            return new FileSet($this->getRef($this->getProject()));
        } else {
            return new FileSet($this);
        }
    }
}
<?php
/*
 *  $Id: d35bf0481e55f713cf67a71878d978f224baa05e $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/types/DataType.php';
include_once 'phing/filters/ConcatFilter.php';
include_once 'phing/filters/HeadFilter.php';
include_once 'phing/filters/IconvFilter.php';
include_once 'phing/filters/TailFilter.php';
include_once 'phing/filters/LineContains.php';
include_once 'phing/filters/LineContainsRegexp.php';
include_once 'phing/filters/EscapeUnicode.php';
include_once 'phing/filters/ExpandProperties.php';
include_once 'phing/filters/PhpArrayMapLines.php';
include_once 'phing/filters/PrefixLines.php';
include_once 'phing/filters/ReplaceRegexp.php';
include_once 'phing/filters/ReplaceTokens.php';
include_once 'phing/filters/ReplaceTokensWithFile.php';
include_once 'phing/filters/SortFilter.php';
include_once 'phing/filters/StripPhpComments.php';
include_once 'phing/filters/StripLineBreaks.php';
include_once 'phing/filters/StripLineComments.php';
include_once 'phing/filters/StripWhitespace.php';
include_once 'phing/filters/SuffixLines.php';
include_once 'phing/filters/TabToSpaces.php';
include_once 'phing/filters/TidyFilter.php';
include_once 'phing/filters/TranslateGettext.php';
include_once 'phing/filters/XincludeFilter.php';
include_once 'phing/filters/XsltFilter.php';

/**
 * FilterChain may contain a chained set of filter readers.
 *
 * @author    Yannick Lecaillez <yl@seasonfive.com>
 * @version   $Id: d35bf0481e55f713cf67a71878d978f224baa05e $
 * @package   phing.types
 */
class FilterChain extends DataType
{

    private $filterReaders = array();

    /**
     * @param null $project
     */
    public function __construct($project = null)
    {
        if ($project) {
            $this->project = $project;
        }
    }

    /**
     * @return array
     */
    public function getFilterReaders()
    {
        return $this->filterReaders;
    }

    /**
     * @param ConcatFilter $o
     */
    public function addConcatFilter(ConcatFilter $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param ExpandProperties $o
     */
    public function addExpandProperties(ExpandProperties $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param TranslateGettext $o
     */
    public function addGettext(TranslateGettext $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param HeadFilter $o
     */
    public function addHeadFilter(HeadFilter $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param IconvFilter $o
     */
    public function addIconvFilter(IconvFilter $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param TailFilter $o
     */
    public function addTailFilter(TailFilter $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param LineContains $o
     */
    public function addLineContains(LineContains $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param LineContainsRegexp $o
     */
    public function addLineContainsRegExp(LineContainsRegexp $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param PrefixLines $o
     */
    public function addPrefixLines(PrefixLines $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param SuffixLines $o
     */
    public function addSuffixLines(SuffixLines $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param PrefixLines $o
     */
    public function addEscapeUnicode(EscapeUnicode $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param PhpArrayMapLines $o
     */
    public function addPhpArrayMapLines(PhpArrayMapLines $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param ReplaceTokens $o
     */
    public function addReplaceTokens(ReplaceTokens $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param ReplaceTokensWithFile $o
     */
    public function addReplaceTokensWithFile(ReplaceTokensWithFile $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param ReplaceRegexp $o
     */
    public function addReplaceRegexp(ReplaceRegexp $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param StripPhpComments $o
     */
    public function addStripPhpComments(StripPhpComments $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param StripLineBreaks $o
     */
    public function addStripLineBreaks(StripLineBreaks $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param StripLineComments $o
     */
    public function addStripLineComments(StripLineComments $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param StripWhitespace $o
     */
    public function addStripWhitespace(StripWhitespace $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param TidyFilter $o
     */
    public function addTidyFilter(TidyFilter $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param TabToSpaces $o
     */
    public function addTabToSpaces(TabToSpaces $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param XincludeFilter $o
     */
    public function addXincludeFilter(XincludeFilter $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param XsltFilter $o
     */
    public function addXsltFilter(XsltFilter $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param PhingFilterReader $o
     */
    public function addFilterReader(PhingFilterReader $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /**
     * @param SortFilter $o
     */
    public function addSortFilter(SortFilter $o)
    {
        $o->setProject($this->project);
        $this->filterReaders[] = $o;
    }

    /*
     * Makes this instance in effect a reference to another FilterChain
     * instance.
     *
     * <p>You must not set another attribute or nest elements inside
     * this element if you make it a reference.</p>
     *
     * @param  $r the reference to which this instance is associated
     * @throws BuildException if this instance already has been configured.
    */
    /**
     * @param Reference $r
     * @throws BuildException
     */
    public function setRefid(Reference $r)
    {

        if (count($this->filterReaders) !== 0) {
            throw $this->tooManyAttributes();
        }

        // change this to get the objects from the other reference
        $o = $r->getReferencedObject($this->getProject());
        if ($o instanceof FilterChain) {
            $this->filterReaders = $o->getFilterReaders();
        } else {
            throw new BuildException($r->getRefId() . " doesn't refer to a FilterChain");
        }
        parent::setRefid($r);
    }
}
<?php
/*
 * $Id: 2fc86a029661c59a2ca1088fd26199d2c4764f73 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * FileSet adapter to SPL's Iterator.
 *
 * @package phing.types
 * @author Alexey Shockov <alexey@shockov.com>
 * @since 2.4.0
 * @internal
 */
class IterableFileSet
    extends FileSet
    implements IteratorAggregate
{
    /**
     * @return Iterator
     */
    public function getIterator()
    {
        return new ArrayIterator($this->getFiles());
    }

    /**
     * @return array
     */
    private function getFiles()
    {
        $directoryScanner = $this->getDirectoryScanner($this->getProject());
        $files = $directoryScanner->getIncludedFiles();

        $baseDirectory = $directoryScanner->getBasedir();
        foreach ($files as $index => $file) {
            $files[$index] = realpath($baseDirectory . '/' . $file);
        }

        return $files;
    }
}
<?php
/*
 *  $Id: f985ca945225b72bc2dd03a0132579cbdff5d6da $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/types/DataType.php';
include_once 'phing/types/Path.php';
include_once 'phing/mappers/CompositeMapper.php';
include_once 'phing/mappers/ContainerMapper.php';

/**
 * Filename Mapper maps source file name(s) to target file name(s).
 *
 * Built-in mappers can be accessed by specifying they "type" attribute:
 * <code>
 * <mapper type="glob" from="*.php" to="*.php.bak"/>
 * </code>
 * Custom mappers can be specified by providing a dot-path to a include_path-relative
 * class:
 * <code>
 * <mapper classname="myapp.mappers.DevToProdMapper" from="*.php" to="*.php"/>
 * <!-- maps all PHP files from development server to production server, for example -->
 * </code>
 *
 * @author Hans Lellelid <hans@xmpl.org>
 * @package phing.types
 */
class Mapper extends DataType
{

    protected $type;
    protected $classname;
    protected $from;
    protected $to;

    /** @var Path $classpath */
    protected $classpath;
    protected $classpathId;

    /** @var ContainerMapper $container */
    private $container = null;

    /**
     * @param Project $project
     */
    public function __construct(Project $project)
    {
        $this->project = $project;
    }

    /**
     * Set the classpath to be used when searching for component being defined
     *
     * @param Path $classpath An Path object containing the classpath.
     * @throws BuildException
     */
    public function setClasspath(Path $classpath)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        if ($this->classpath === null) {
            $this->classpath = $classpath;
        } else {
            $this->classpath->append($classpath);
        }
    }

    /**
     * Create the classpath to be used when searching for component being defined
     */
    public function createClasspath()
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        if ($this->classpath === null) {
            $this->classpath = new Path($this->project);
        }

        return $this->classpath->createPath();
    }

    /**
     * Reference to a classpath to use when loading the files.
     * @param Reference $r
     * @throws BuildException
     */
    public function setClasspathRef(Reference $r)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        $this->classpathId = $r->getRefId();
        $this->createClasspath()->setRefid($r);
    }

    /**
     * Set the type of FileNameMapper to use.
     * @param $type
     * @throws BuildException
     */
    public function setType($type)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        $this->type = $type;
    }

    /**
     * Add a nested <code>FileNameMapper</code>.
     * @param FileNameMapper $fileNameMapper the <code>FileNameMapper</code> to add.
     * @throws BuildException
     */
    public function add(Mapper $fileNameMapper)
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }
        if ($this->container == null) {
            if ($this->type == null && $this->classname == null) {
                $this->container = new CompositeMapper();
            } else {
                $m = $this->getImplementation();
                if ($m instanceof ContainerMapper) {
                    $this->container = $m;
                } else {
                    throw new BuildException("$m mapper implementation does not support nested mappers!");
                }
            }
        }
        $this->container->add($fileNameMapper);
        $this->checked = false;
    }

    /**
     * Add a Mapper
     * @param Mapper $mapper the mapper to add
     */
    public function addMapper(Mapper $mapper)
    {
        $this->add($mapper);
    }

    /**
     * Set the class name of the FileNameMapper to use.
     * @param string $classname
     * @throws BuildException
     */
    public function setClassname($classname)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        $this->classname = $classname;
    }

    /**
     * Set the argument to FileNameMapper.setFrom
     * @param $from
     * @throws BuildException
     */
    public function setFrom($from)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        $this->from = $from;
    }

    /**
     * Set the argument to FileNameMapper.setTo
     * @param $to
     * @throws BuildException
     */
    public function setTo($to)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        $this->to = $to;
    }

    /**
     * Make this Mapper instance a reference to another Mapper.
     *
     * You must not set any other attribute if you make it a reference.
     * @param Reference $r
     * @throws BuildException
     */
    public function setRefid(Reference $r)
    {
        if ($this->type !== null || $this->from !== null || $this->to !== null) {
            throw DataType::tooManyAttributes();
        }
        parent::setRefid($r);
    }

    /** Factory, returns inmplementation of file name mapper as new instance */
    public function getImplementation()
    {
        if ($this->isReference()) {
            $o = $this->getRef();
            if ($o instanceof FileNameMapper) {
                return $o;
            }
            if ($o instanceof Mapper) {
                return $o->getImplementation();
            }

            $od = $o == null ? "null" : get_class($o);
            throw new BuildException($od . " at reference '" . $r->getRefId() . "' is not a valid mapper reference.");
        }

        if ($this->type === null && $this->classname === null && $this->container == null) {
            throw new BuildException("either type or classname attribute must be set for <mapper>");
        }

        if ($this->container != null) {
            return $this->container;
        }

        if ($this->type !== null) {
            switch ($this->type) {
                case 'chained':
                    $this->classname = 'phing.mappers.ChainedMapper';
                    break;
                case 'composite':
                    $this->classname = 'phing.mappers.CompositeMapper';
                    break;
                case 'cutdirs':
                    $this->classname = 'phing.mappers.CutDirsMapper';
                    break;
                case 'identity':
                    $this->classname = 'phing.mappers.IdentityMapper';
                    break;
                case 'firstmatch':
                    $this->classname = 'phing.mappers.FirstMatchMapper';
                    break;
                case 'flatten':
                    $this->classname = 'phing.mappers.FlattenMapper';
                    break;
                case 'glob':
                    $this->classname = 'phing.mappers.GlobMapper';
                    break;
                case 'regexp':
                case 'regex':
                    $this->classname = 'phing.mappers.RegexpMapper';
                    break;
                case 'merge':
                    $this->classname = 'phing.mappers.MergeMapper';
                    break;
                default:
                    throw new BuildException("Mapper type {$this->type} not known");
                    break;
            }
        }

        // get the implementing class
        $cls = Phing::import($this->classname, $this->classpath);

        $m = new $cls();
        $m->setFrom($this->from);
        $m->setTo($this->to);

        return $m;
    }

    /** Performs the check for circular references and returns the referenced Mapper. */
    private function getRef()
    {
        if (!$this->checked) {
            $stk = array();
            $stk[] = $this;
            $this->dieOnCircularReference($stk, $this->project);
        }

        $o = $this->ref->getReferencedObject($this->project);
        if (!($o instanceof Mapper)) {
            $msg = $this->ref->getRefId() . " doesn't denote a mapper";
            throw new BuildException($msg);
        } else {
            return $o;
        }
    }
}
<?php
/*
 *  $Id: 4206aed7478704fa7ef5d066ba183159ae0ea7f9 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/types/DataType.php';

/**
 * A parameter is composed of a name, type and value. Nested
 * Parameters are also possible, but the using task/type has
 * to support them
 *
 * @author    Manuel Holtgrewe
 * @author    <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a>
 * @package   phing.types
 */
class Parameter extends DataType
{

    /** Parameter name */
    protected $name;

    /** Parameter type */
    protected $type;

    /** Parameter value */
    protected $value;

    /** Nested parameters */
    protected $parameters = array();

    /**
     * @param $name
     */
    public function setName($name)
    {
        $this->name = (string) $name;
    }

    /**
     * @param $type
     */
    public function setType($type)
    {
        $this->type = (string) $type;
    }

    /**
     * Sets value to dynamic register slot.
     * @param RegisterSlot $value
     */
    public function setListeningValue(RegisterSlot $value)
    {
        $this->value = $value;
    }

    /**
     * @param $value
     */
    public function setValue($value)
    {
        $this->value = (string) $value;
    }

    public function getName()
    {
        return $this->name;
    }

    public function getType()
    {
        return $this->type;
    }

    /**
     * @return mixed
     */
    public function getValue()
    {
        if ($this->value instanceof RegisterSlot) {
            return $this->value->getValue();
        } else {
            return $this->value;
        }
    }

    /**
     * @return Parameter
     */
    public function createParam()
    {
        $num = array_push($this->parameters, new Parameter());

        return $this->parameters[$num - 1];
    }

    /**
     * @return array Nested parameters.
     */
    public function getParams()
    {
        return $this->parameters;
    }
}
<?php

/*
 * $Id: cb26c02b1c8efb2408fecd8c36b2aa5d18d37977 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Parameterizable objects take genric key value pairs.
 *
 * @author Hans Lellelid, hans@xmpl.org (Phing)
 * @author Magesh Umasankar (Ant)
 * @package phing.types
 */
interface Parameterizable
{
    /**
     * @param $parameters
     * @return mixed
     */
    public function setParameters($parameters);
}
<?php
/*
 *  $Id: a49cf8d88ef1fb89fbef5d26107432cf17bd072b $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/DataType.php';
include_once 'phing/util/PathTokenizer.php';
include_once 'phing/types/FileSet.php';

/**
 * This object represents a path as used by include_path or PATH
 * environment variable.
 *
 * This class has been adopted from the Java Ant equivalent.  The ability have
 * path structures in Phing is important; however, because of how PHP classes interact
 * the ability to specify CLASSPATHs makes less sense than Java.Rather than providing
 * CLASSPATH for any tasks that take classes as parameters, perhaps a better
 * solution in PHP is to have an IncludePath task, which prepends paths to PHP's include_path
 * INI variable. This gets around the problem that simply using a path to load the initial
 * PHP class is not enough (in most cases the loaded class may assume that it is on the global
 * PHP include_path, and will try to load dependent classes accordingly).  The other option is
 * to provide a way for this class to add paths to the include path, if desired -- or to create
 * an IncludePath subclass.  Once added, though, when would a path be removed from the include path?
 *
 * <p>
 * <code>
 * &lt;sometask&gt;<br>
 * &nbsp;&nbsp;&lt;somepath&gt;<br>
 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/path/to/file" /&gt;<br>
 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement path="/path/to/class2;/path/to/class3" /&gt;<br>
 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/path/to/file3" /&gt;<br>
 * &nbsp;&nbsp;&lt;/somepath&gt;<br>
 * &lt;/sometask&gt;<br>
 * </code>
 * <p>
 * The object implemention <code>sometask</code> must provide a method called
 * <code>createSomepath</code> which returns an instance of <code>Path</code>.
 * Nested path definitions are handled by the Path object and must be labeled
 * <code>pathelement</code>.<p>
 *
 * The path element takes a parameter <code>path</code> which will be parsed
 * and split into single elements. It will usually be used
 * to define a path from an environment variable.
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Thomas.Haas@softwired-inc.com (Ant)
 * @author Stefan Bodewig <stefan.bodewig@epost.de> (Ant)
 * @package phing.types
 */
class Path extends DataType
{

    private $elements = array();

    /**
     * Constructor for internally instantiated objects sets project.
     * @param Project $project
     * @param string  $path    (for use by IntrospectionHelper)
     */
    public function __construct($project = null, $path = null)
    {
        if ($project !== null) {
            $this->setProject($project);
        }
        if ($path !== null) {
            $this->createPathElement()->setPath($path);
        }
    }

    /**
     * Adds a element definition to the path.
     *
     * @param PhingFile $location the location of the element to add (must not be
     *                            <code>null</code> nor empty.
     *
     * @return void
     *
     * @throws BuildException
     */
    public function setDir(PhingFile $location)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        $this->createPathElement()->setDir($location);
    }

    /**
     * Parses a path definition and creates single PathElements.
     *
     * @param $path the path definition.
     *
     * @throws BuildException
     */
    public function setPath($path)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        $this->createPathElement()->setPath($path);
    }

    /**
     * Makes this instance in effect a reference to another Path instance.
     *
     * <p>You must not set another attribute or nest elements inside
     * this element if you make it a reference.</p>
     *
     * @param Reference $r
     *
     * @return void
     *
     * @throws BuildException
     */
    public function setRefid(Reference $r)
    {
        if (!empty($this->elements)) {
            throw $this->tooManyAttributes();
        }
        $this->elements[] = $r;
        parent::setRefid($r);
    }

    /**
     * Creates the nested <code>&lt;pathelement&gt;</code> element.
     *
     * @return void
     *
     * @throws BuildException
     */
    public function createPathElement()
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }
        $pe = new PathElement($this);
        $this->elements[] = $pe;

        return $pe;
    }

    /**
     * Adds a nested <code>&lt;fileset&gt;</code> element.
     *
     * @param FileSet $fs
     *
     * @return void
     *
     * @throws BuildException
     */
    public function addFileset(FileSet $fs)
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }
        $this->elements[] = $fs;
        $this->checked = false;
    }

    /**
     * Adds a nested <code>&lt;dirset&gt;</code> element.
     *
     * @param DirSet $dset
     *
     * @return void
     *
     * @throws BuildException
     */
    public function addDirset(DirSet $dset)
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }
        $this->elements[] = $dset;
        $this->checked = false;
    }

    /**
     * Creates a nested <code>&lt;path&gt;</code> element.
     *
     * @return Path
     *
     * @throws BuildException
     */
    public function createPath()
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }
        $p = new Path($this->project);
        $this->elements[] = $p;
        $this->checked = false;

        return $p;
    }

    /**
     * Append the contents of the other Path instance to this.
     *
     * @param Path $other
     *
     * @return void
     *
     * @throws BuildException
     */
    public function append(Path $other)
    {
        if ($other === null) {
            return;
        }
        $l = $other->listPaths();
        foreach ($l as $path) {
            if (!in_array($path, $this->elements, true)) {
                $this->elements[] = $path;
            }
        }
    }

    /**
     * Adds the components on the given path which exist to this
     * Path. Components that don't exist, aren't added.
     *
     * @param Path $source - Source path whose components are examined for existence.
     *
     * @return void
     */
    public function addExisting(Path $source)
    {
        $list = $source->listPaths();
        foreach ($list as $el) {
            $f = null;
            if ($this->project !== null) {
                $f = $this->project->resolveFile($el);
            } else {
                $f = new PhingFile($el);
            }

            if ($f->exists()) {
                $this->setDir($f);
            } else {
                $this->log(
                    "dropping " . $f->__toString() . " from path as it doesn't exist",
                    Project::MSG_VERBOSE
                );
            }
        }
    }

    /**
     * Returns all path elements defined by this and nested path objects.
     *
     * @throws BuildException
     *
     * @return array List of path elements.
     */
    public function listPaths()
    {
        if (!$this->checked) {
            // make sure we don't have a circular reference here
            $stk = array();
            array_push($stk, $this);
            $this->dieOnCircularReference($stk, $this->project);
        }

        $result = array();
        for ($i = 0, $elSize = count($this->elements); $i < $elSize; $i++) {
            $o = $this->elements[$i];
            if ($o instanceof Reference) {
                $refId = $o->getRefId();
                $o = $o->getReferencedObject($this->project);
                // we only support references to paths right now
                if (!($o instanceof Path)) {
                    $msg = $refId . " doesn't denote a path";
                    throw new BuildException($msg);
                }
            }

            if (is_string($o)) {
                $result[] = $o;
            } elseif ($o instanceof PathElement) {
                $parts = $o->getParts();
                if ($parts === null) {
                    throw new BuildException("You must either set location or"
                        . " path on <pathelement>");
                }
                foreach ($parts as $part) {
                    $result[] = $part;
                }
            } elseif ($o instanceof Path) {
                $p = $o;
                if ($p->getProject() === null) {
                    $p->setProject($this->getProject());
                }
                $parts = $p->listPaths();
                foreach ($parts as $part) {
                    $result[] = $part;
                }
            } elseif ($o instanceof DirSet) {
                $dset = $o;
                $ds = $dset->getDirectoryScanner($this->project);
                $dirstrs = $ds->getIncludedDirectories();
                $dir = $dset->getDir($this->project);
                foreach ($dirstrs as $dstr) {
                    $d = new PhingFile($dir, $dstr);
                    $result[] = $d->getAbsolutePath();
                }
            } elseif ($o instanceof FileSet) {
                $fs = $o;
                $ds = $fs->getDirectoryScanner($this->getProject());
                $filestrs = $ds->getIncludedFiles();
                $dir = $fs->getDir($this->getProject());
                foreach ($filestrs as $fstr) {
                    $d = new PhingFile($dir, $fstr);
                    $result[] = $d->getAbsolutePath();
                }
            } elseif ($o instanceof FileList) {
                $fl = $o;
                $dirstrs = $fl->getFiles($this->project);
                $dir = $fl->getDir($this->project);
                foreach ($dirstrs as $dstr) {
                    $d = new PhingFile($dir, $dstr);
                    $result[] = $d->getAbsolutePath();
                }
            }
        }

        return array_unique($result);
    }


    /**
     * Returns a textual representation of the path, which can be used as
     * CLASSPATH or PATH environment variable definition.
     *
     * @return string A textual representation of the path.
     */
    public function __toString()
    {

        $list = $this->listPaths();

        // empty path return empty string
        if (empty($list)) {
            return "";
        }

        return implode(PATH_SEPARATOR, $list);
    }

    /**
     * Splits a PATH (with : or ; as separators) into its parts.
     *
     * @param Project $project
     * @param string $source
     *
     * @return array
     */
    public static function translatePath(Project $project, $source)
    {
        $result = array();
        if ($source == null) {
            return "";
        }

        $tok = new PathTokenizer($source);
        while ($tok->hasMoreTokens()) {
            $pathElement = $tok->nextToken();
            try {
                $element = self::resolveFile($project, $pathElement);
                for ($i = 0, $_i = strlen($element); $i < $_i; $i++) {
                    self::translateFileSep($element, $i);
                }
                $result[] = $element;
            } catch (BuildException $e) {
                $project->log(
                    "Dropping path element " . $pathElement
                    . " as it is not valid relative to the project",
                    Project::MSG_VERBOSE
                );
            }
        }

        return $result;
    }

    /**
     * Returns its argument with all file separator characters
     * replaced so that they match the local OS conventions.
     *
     * @param string $source
     *
     * @return string
     */
    public static function translateFile($source)
    {
        if ($source == null) {
            return "";
        }

        $result = $source;
        for ($i = 0, $_i = strlen($source); $i < $_i; $i++) {
            self::translateFileSep($result, $i);
        }

        return $result;
    }

    /**
     * Translates all occurrences of / or \ to correct separator of the
     * current platform and returns whether it had to do any
     * replacements.
     *
     * @param string $buffer
     * @param int $pos
     *
     * @return bool
     */
    protected static function translateFileSep(&$buffer, $pos)
    {
        if ($buffer{$pos} == '/' || $buffer{$pos} == '\\') {
            $buffer{$pos} = DIRECTORY_SEPARATOR;

            return true;
        }

        return false;
    }

    /**
     * How many parts does this Path instance consist of.
     * DEV NOTE: expensive call! list is generated, counted, and then
     * discareded.
     *
     * @return int
     */
    public function size()
    {
        return count($this->listPaths());
    }

    /**
     * Return a Path that holds the same elements as this instance.
     *
     * @return Path
     */
    public function __clone()
    {
        $p = new Path($this->project);
        $p->append($this);

        return $p;
    }

    /**
     * Overrides the version of DataType to recurse on all DataType
     * child elements that may have been added.
     *
     * @param $stk
     * @param Project $p
     *
     * @return void
     *
     * @throws BuildException
     */
    public function dieOnCircularReference(&$stk, Project $p)
    {

        if ($this->checked) {
            return;
        }

        // elements can contain strings, FileSets, Reference, etc.
        foreach ($this->elements as $o) {

            if ($o instanceof Reference) {
                $o = $o->getReferencedObject($p);
            }

            if ($o instanceof DataType) {
                if (in_array($o, $stk, true)) {
                    throw $this->circularReference();
                } else {
                    array_push($stk, $o);
                    $o->dieOnCircularReference($stk, $p);
                    array_pop($stk);
                }
            }
        }

        $this->checked = true;
    }

    /**
     * Resolve a filename with Project's help - if we know one that is.
     *
     * <p>Assume the filename is absolute if project is null.</p>
     *
     * @param Project $project
     * @param $relativeName
     *
     * @return string
     */
    private static function resolveFile(Project $project, $relativeName)
    {
        if ($project !== null) {
            $f = $project->resolveFile($relativeName);

            return $f->getAbsolutePath();
        }

        return $relativeName;
    }
}

/**
 * Helper class, holds the nested <code>&lt;pathelement&gt;</code> values.
 *
 * @package phing.types
 */
class PathElement
{
    /** @var array $parts */
    private $parts = array();

    /** @var Path $outer */
    private $outer;

    /**
     * @param Path $outer
     */
    public function __construct(Path $outer)
    {
        $this->outer = $outer;
    }

    /**
     * @param PhingFile $loc
     *
     * @return void
     */
    public function setDir(PhingFile $loc)
    {
        $this->parts = array(Path::translateFile($loc->getAbsolutePath()));
    }

    /**
     * @param $path
     *
     * @return void
     */
    public function setPath($path)
    {
        $this->parts = Path::translatePath($this->outer->getProject(), $path);
    }

    /**
     * @return array
     */
    public function getParts()
    {
        return $this->parts;
    }
}
<?php
/*
 *  $Id: b070eed1e9ac42f7ebd826ff7f3081f45e7c944d $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/system/io/FileReader.php';
include_once 'phing/types/DataType.php';

/**
 * The patternset storage component. Carries all necessary data and methods
 * for the patternset stuff.
 *
 * @author   Andreas Aderhold, andi@binarycloud.com
 * @version  $Id: b070eed1e9ac42f7ebd826ff7f3081f45e7c944d $
 * @package  phing.types
 */
class PatternSet extends DataType
{

    private $includeList = array();
    private $excludeList = array();
    private $includesFileList = array();
    private $excludesFileList = array();

    /**
     * Makes this instance in effect a reference to another PatternSet
     * instance.
     * You must not set another attribute or nest elements inside
     * this element if you make it a reference.
     * @param Reference $r
     * @throws BuildException
     */
    public function setRefid(Reference $r)
    {
        if (!empty($this->includeList) || !empty($this->excludeList)) {
            throw $this->tooManyAttributes();
        }
        parent::setRefid($r);
    }

    /**
     * Add a name entry on the include list
     *
     * @return PatternSetNameEntry Reference to object
     * @throws BuildException
     */
    public function createInclude()
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }

        return $this->addPatternToList($this->includeList);
    }

    /**
     * Add a name entry on the include files list
     *
     * @return PatternSetNameEntry Reference to object
     * @throws BuildException
     */
    public function createIncludesFile()
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }

        return $this->addPatternToList($this->includesFileList);
    }

    /**
     * Add a name entry on the exclude list
     *
     * @return PatternSetNameEntry Reference to object
     * @throws BuildException
     */
    public function createExclude()
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }

        return $this->addPatternToList($this->excludeList);
    }

    /**
     * add a name entry on the exclude files list
     *
     * @return PatternSetNameEntry Reference to object
     * @throws BuildException
     */
    public function createExcludesFile()
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }

        return $this->addPatternToList($this->excludesFileList);
    }

    /**
     * Sets the set of include patterns. Patterns may be separated by a comma
     * or a space.
     *
     * @param  string $includes the string containing the include patterns
     * @return void
     * @throws BuildException
     */
    public function setIncludes($includes)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        if ($includes !== null && strlen($includes) > 0) {
            $tok = strtok($includes, ", ");
            while ($tok !== false) {
                $o = $this->createInclude();
                $o->setName($tok);
                $tok = strtok(", ");
            }
        }
    }

    /**
     * Sets the set of exclude patterns. Patterns may be separated by a comma
     * or a space.
     *
     * @param  string the string containing the exclude patterns
     * @return void
     * @throws BuildException
     */
    public function setExcludes($excludes)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        if ($excludes !== null && strlen($excludes) > 0) {
            $tok = strtok($excludes, ", ");
            while ($tok !== false) {
                $o = $this->createExclude();
                $o->setName($tok);
                $tok = strtok(", ");
            }
        }
    }

    /**
     * add a name entry to the given list
     *
     * @param  array List onto which the nameentry should be added
     * @return PatternSetNameEntry Reference to the created PsetNameEntry instance
     */
    private function addPatternToList(&$list)
    {
        $num = array_push($list, new PatternSetNameEntry());

        return $list[$num - 1];
    }

    /**
     * Sets the name of the file containing the includes patterns.
     *
     * @param PhingFile $includesFile file to fetch the include patterns from.
     *
     * @throws BuildException
     */
    public function setIncludesFile($includesFile)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        if ($includesFile instanceof PhingFile) {
            $includesFile = $includesFile->getPath();
        }
        $o = $this->createIncludesFile();
        $o->setName($includesFile);
    }

    /**
     * Sets the name of the file containing the excludes patterns.
     *
     * @param PhingFile $excludesFile file to fetch the exclude patterns from.
     * @throws BuildException
     */
    public function setExcludesFile($excludesFile)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        if ($excludesFile instanceof PhingFile) {
            $excludesFile = $excludesFile->getPath();
        }
        $o = $this->createExcludesFile();
        $o->setName($excludesFile);
    }

    /**
     * Reads path matching patterns from a file and adds them to the
     * includes or excludes list
     *
     * @param PhingFile $patternfile
     * @param $patternlist
     * @param Project $p
     *
     * @throws BuildException
     */
    private function readPatterns(PhingFile $patternfile, &$patternlist, Project $p)
    {
        $patternReader = null;
        try {
            // Get a FileReader
            $patternReader = new BufferedReader(new FileReader($patternfile));

            // Create one NameEntry in the appropriate pattern list for each
            // line in the file.
            $line = $patternReader->readLine();
            while ($line !== null) {
                if (!empty($line)) {
                    $line = $p->replaceProperties($line);
                    $this->addPatternToList($patternlist)->setName($line);
                }
                $line = $patternReader->readLine();
            }

        } catch (IOException $ioe) {
            $msg = "An error occurred while reading from pattern file: " . $patternfile->__toString();
            if ($patternReader) {
                $patternReader->close();
            }
            throw new BuildException($msg, $ioe);
        }

        $patternReader->close();
    }

    /**
     * Adds the patterns of the other instance to this set.
     *
     * @param $other
     * @param Project $p
     *
     * @throws BuildException
     */
    public function append($other, $p)
    {
        if ($this->isReference()) {
            throw new BuildException("Cannot append to a reference");
        }

        $incl = $other->getIncludePatterns($p);
        if ($incl !== null) {
            foreach ($incl as $incl_name) {
                $o = $this->createInclude();
                $o->setName($incl_name);
            }
        }

        $excl = $other->getExcludePatterns($p);
        if ($excl !== null) {
            foreach ($excl as $excl_name) {
                $o = $this->createExclude();
                $o->setName($excl_name);
            }
        }
    }

    /**
     * Returns the filtered include patterns.
     *
     * @param Project $p
     *
     * @throws BuildException
     *
     * @return array
     */
    public function getIncludePatterns(Project $p)
    {
        if ($this->isReference()) {
            $o = $this->getRef($p);

            return $o->getIncludePatterns($p);
        } else {
            $this->readFiles($p);

            return $this->makeArray($this->includeList, $p);
        }
    }

    /**
     * Returns the filtered exclude patterns.
     *
     * @param Project $p
     *
     * @throws BuildException
     *
     * @return array
     */
    public function getExcludePatterns(Project $p)
    {
        if ($this->isReference()) {
            $o = $this->getRef($p);

            return $o->getExcludePatterns($p);
        } else {
            $this->readFiles($p);

            return $this->makeArray($this->excludeList, $p);
        }
    }

    /**
     * helper for FileSet.
     *
     * @return bool
     */
    public function hasPatterns()
    {
        return (boolean) count($this->includesFileList) > 0 || count($this->excludesFileList) > 0
        || count($this->includeList) > 0 || count($this->excludeList) > 0;
    }

    /**
     * Performs the check for circular references and returns the
     * referenced PatternSet.
     *
     * @param Project $p
     *
     * @throws BuildException
     *
     * @return Reference
     */
    public function getRef(Project $p)
    {
        if (!$this->checked) {
            $stk = array();
            array_push($stk, $this);
            $this->dieOnCircularReference($stk, $p);
        }
        $o = $this->ref->getReferencedObject($p);
        if (!($o instanceof PatternSet)) {
            $msg = $this->ref->getRefId() . " doesn't denote a patternset";
            throw new BuildException($msg);
        } else {
            return $o;
        }
    }

    /**
     * Convert a array of PatternSetNameEntry elements into an array of Strings.
     *
     * @param array $list
     * @param Project $p
     *
     * @return array
     */
    private function makeArray(&$list, Project $p)
    {

        if (count($list) === 0) {
            return null;
        }

        $tmpNames = array();
        foreach ($list as $ne) {
            $pattern = (string) $ne->evalName($p);
            if ($pattern !== null && strlen($pattern) > 0) {
                array_push($tmpNames, $pattern);
            }
        }

        return $tmpNames;
    }

    /**
     * Read includesfile or excludesfile if not already done so.
     *
     * @param Project $p
     *
     * @throws BuildException
     */
    private function readFiles(Project $p)
    {
        if (!empty($this->includesFileList)) {
            foreach ($this->includesFileList as $ne) {
                $fileName = (string) $ne->evalName($p);
                if ($fileName !== null) {
                    $inclFile = $p->resolveFile($fileName);
                    if (!$inclFile->exists()) {
                        throw new BuildException("Includesfile " . $inclFile->getAbsolutePath() . " not found.");
                    }
                    $this->readPatterns($inclFile, $this->includeList, $p);
                }
            }
            $this->includesFileList = array();
        }

        if (!empty($this->excludesFileList)) {
            foreach ($this->excludesFileList as $ne) {
                $fileName = (string) $ne->evalName($p);
                if ($fileName !== null) {
                    $exclFile = $p->resolveFile($fileName);
                    if (!$exclFile->exists()) {
                        throw new BuildException("Excludesfile " . $exclFile->getAbsolutePath() . " not found.");
                    }
                    $this->readPatterns($exclFile, $this->excludeList, $p);
                }
            }
            $this->excludesFileList = array();
        }
    }

    /**
     * @return string
     */
    public function toString()
    {

        // We can't compile includeList into array because, toString() does
        // not know about project:
        //
        // $includes = $this->makeArray($this->includeList, $this->project);
        // $excludes = $this->makeArray($this->excludeList, $this->project);

        if (empty($this->includeList)) {
            $includes = "empty";
        } else {
            $includes = "";
            foreach ($this->includeList as $ne) {
                $includes .= $ne->toString() . ",";
            }
            $includes = rtrim($includes, ",");
        }

        if (empty($this->excludeList)) {
            $excludes = "empty";
        } else {
            $excludes = "";
            foreach ($this->excludeList as $ne) {
                $excludes .= $ne->toString() . ",";
            }
            $excludes = rtrim($excludes, ",");
        }

        return "patternSet{ includes: $includes  excludes: $excludes }";
    }
}

/**
 * "Internal" class for holding an include/exclude pattern.
 *
 * @package  phing.types
 */
class PatternSetNameEntry
{

    /**
     * The pattern.
     * @var string
     */
    private $name;

    /**
     * The if-condition property for this pattern to be applied.
     * @var string
     */
    private $ifCond;

    /**
     * The unless-condition property for this pattern to be applied.
     * @var string
     */
    private $unlessCond;

    /**
     * An alias for the setName() method.
     * @see setName()
     * @param string $pattern
     */
    public function setPattern($pattern)
    {
        $this->setName($pattern);
    }

    /**
     * Set the pattern text.
     * @param string $name The pattern
     */
    public function setName($name)
    {
        $this->name = (string) $name;
    }

    /**
     * Sets an if-condition property for this pattern to match.
     * @param string $cond
     */
    public function setIf($cond)
    {
        $this->ifCond = (string) $cond;
    }

    /**
     * Sets an unless-condition property for this pattern to match.
     * @param string $cond
     */
    public function setUnless($cond)
    {
        $this->unlessCond = (string) $cond;
    }

    /**
     * Get the pattern text.
     * @return string The pattern.
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Evaluates the pattern.
     * @param Project $project
     * @return string The pattern or null if it is ruled out by a condition.
     */
    public function evalName(Project $project)
    {
        return $this->valid($project) ? $this->name : null;
    }

    /**
     * Checks whether pattern should be applied based on whether the if and unless
     * properties are set in project.
     * @param  Project $project
     * @return boolean
     */
    public function valid(Project $project)
    {
        if ($this->ifCond !== null && $project->getProperty($this->ifCond) === null) {
            return false;
        } else {
            if ($this->unlessCond !== null && $project->getProperty($this->unlessCond) !== null) {
                return false;
            }
        }

        return true;
    }

    /**
     * Gets a string representation of this pattern.
     * @return string
     */
    public function toString()
    {
        $buf = $this->name;
        if (($this->ifCond !== null) || ($this->unlessCond !== null)) {
            $buf .= ":";
            $connector = "";

            if ($this->ifCond !== null) {
                $buf .= "if->{$this->ifCond}";
                $connector = ";";
            }
            if ($this->unlessCond !== null) {
                $buf .= "$connector unless->{$this->unlessCond}";
            }
        }

        return $buf;
    }
}
<?php
/**
 * Part of phing, the PHP build tool
 *
 * PHP version 5
 *
 * @category Types
 * @package  phing.types
 * @author   Christian Weiske <cweiske@cweiske.de>
 * @license  LGPL v3 or later http://www.gnu.org/licenses/lgpl.html
 * @version  SVN: $Id: 87afe1e8834ed20bf9005a2bc6971daedd60e628 $
 * @link     http://www.phing.info/
 */
require_once 'phing/types/FileSet.php';
require_once 'phing/util/PearPackageScanner.php';

/**
 * Fileset that contains files of an installed PEAR package.
 * It can be used to package up PEAR package dependencies in own
 * release files (zip, tgz, phar).
 *
 * @internal
 * A normal fileset is used that way in CopyTask, rSTTask:
 * <code>
 *  $ds = $fs->getDirectoryScanner($project);
 *  $fromDir  = $fs->getDir($project);
 *  $srcFiles = $ds->getIncludedFiles();
 *  $srcDirs  = $ds->getIncludedDirectories();
 * </code>
 * The scanner is used as follows:
 * <code>
 *  $ds->getBaseDir()
 *  $ds->scan()
 * </code>
 *
 * @category Types
 * @package  phing.types
 * @author   Christian Weiske <cweiske@cweiske.de>
 * @license  LGPL v3 or later http://www.gnu.org/licenses/lgpl.html
 * @link     http://www.phing.info/
 */
class PearPackageFileSet extends FileSet
{
    /**
     * Name of channel the package is from, e.g. "pear.php.net".
     *
     * @var string
     */
    protected $channel;

    /**
     * Package name to get files from, e.g. "Console_CommandLine"
     *
     * @var string
     */
    protected $package;

    /**
     * File to use for generated package.xml
     *
     * @var string
     */
    protected $packageFile = '';

    /**
     * Use files of that role only.
     * Multiple roles are not supported, and you always have to specify one.
     *
     * @var string
     */
    protected $role = 'php';

    /**
     * Full path to a PEAR config file.
     * If none provided, default one is used.
     *
     * @var string
     */
    protected $config;

    /**
     * @var PearPackageScanner instance
     */
    protected $pps;

    /**
     * Creates and returns the pear package scanner.
     * Scanner already has scan() called.
     *
     * @param Project $p Current phing project
     *
     * @return PearPackageScanner
     */
    public function getDirectoryScanner(Project $p)
    {
        if ($this->isReference()) {
            $obj = $this->getRef($p);

            return $obj->getDirectoryScanner($p);
        }

        $this->loadPearPackageScanner($p);

        return $this->pps;
    }

    /**
     * Returns the base directory all package files are relative to
     *
     * @param Project $p Current phing project
     *
     * @return PhingFile Base directory
     */
    public function getDir(Project $p)
    {
        if ($this->pps === null) {
            $this->loadPearPackageScanner($p);
        }

        return new PhingFile((string) $this->pps->getBaseDir());
    }

    /**
     * Loads the package scanner instance into $this->pps
     *
     * @param Project $p Current phing project
     *
     * @return void
     */
    protected function loadPearPackageScanner(Project $p)
    {
        $this->pps = new PearPackageScanner();
        $this->pps->setDescFile($this->packageFile);
        $this->pps->setPackage($this->package);
        $this->pps->setChannel($this->channel);
        $this->pps->setRole($this->role);
        $this->pps->setConfig($this->config);
        $this->pps->setIncludes($this->defaultPatterns->getIncludePatterns($p));
        $this->pps->setExcludes($this->defaultPatterns->getExcludePatterns($p));
        $this->pps->scan();
    }

    /**
     * Sets the package.xml filename.
     * If it is not set, the local pear installation is queried for the package.
     *
     * @param $descFile
     * @return void
     */
    public function setDescFile($descFile)
    {
        $this->packageFile = $descFile;
    }

    /**
     * Sets the package name.
     * If no channel is given, "pear.php.net" is used.
     *
     * @param string $package Single package name, or "channel/name" combination
     *
     * @throws BuildException
     * @return void
     */
    public function setPackage($package)
    {
        $parts = explode('/', $package);
        if (count($parts) > 2) {
            throw new BuildException('Invalid package name: ' . $package);
        }

        if (count($parts) == 1) {
            $this->channel = 'pear.php.net';
            $this->package = $parts[0];
        } else {
            $this->channel = $parts[0];
            $this->package = $parts[1];
        }
    }

    /**
     * Sets the role of files that should be included.
     * Examples are php,doc,script
     *
     * @param string $role PEAR file role
     *
     * @return void
     */
    public function setRole($role)
    {
        $this->role = $role;
    }

    /**
     * Sets the full path to the PEAR configuration file
     *
     * @param string $config Configuration file
     *
     * @return void
     */
    public function setConfig($config)
    {
        $this->config = $config;
    }
}
<?php
/*
 *  $Id: 31c0eed7c2c8a83cd5055890b2034ae83089452e $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/types/DataType.php';
include_once 'phing/types/Parameter.php';

/**
 * A PhingFilterReader is a wrapper class that encloses the className
 * and configuration of a Configurable FilterReader.
 *
 * @author    Yannick Lecaillez <yl@seasonfive.com>
 * @version   $Id: 31c0eed7c2c8a83cd5055890b2034ae83089452e $
 * @see       FilterReader
 * @package   phing.types
 */
class PhingFilterReader extends DataType
{

    private $className;
    private $parameters = array();
    private $classPath;

    /**
     * @param $className
     */
    public function setClassName($className)
    {
        $this->className = $className;
    }

    public function getClassName()
    {
        return $this->className;
    }

    /**
     * Set the classpath to load the FilterReader through (attribute).
     * @param Path $classpath
     * @throws BuildException
     */
    public function setClasspath(Path $classpath)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        if ($this->classPath === null) {
            $this->classPath = $classpath;
        } else {
            $this->classPath->append($classpath);
        }
    }

    /*
     * Set the classpath to load the FilterReader through (nested element).
    */
    /**
     * @return Path
     * @throws BuildException
     */
    public function createClasspath()
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }
        if ($this->classPath === null) {
            $this->classPath = new Path($this->project);
        }

        return $this->classPath->createPath();
    }

    public function getClasspath()
    {
        return $this->classPath;
    }

    /**
     * @param Reference $r
     * @throws BuildException
     */
    public function setClasspathRef(Reference $r)
    {
        if ($this->isReference()) {
            throw $this->tooManyAttributes();
        }
        $o = $this->createClasspath();
        $o->setRefid($r);
    }

    /**
     * @param Parameter $param
     */
    public function addParam(Parameter $param)
    {
        $this->parameters[] = $param;
    }

    public function createParam()
    {
        $num = array_push($this->parameters, new Parameter());

        return $this->parameters[$num - 1];
    }

    /**
     * @return array
     */
    public function getParams()
    {
        // We return a COPY
        $ret = array();
        for ($i = 0, $size = count($this->parameters); $i < $size; $i++) {
            $ret[] = clone $this->parameters[$i];
        }

        return $ret;
    }

    /*
     * Makes this instance in effect a reference to another PhingFilterReader
     * instance.
     *
     * <p>You must not set another attribute or nest elements inside
     * this element if you make it a reference.</p>
     *
     * @param Reference $r the reference to which this instance is associated
     * @exception BuildException if this instance already has been configured.
    */
    /**
     * @param Reference $r
     * @throws BuildException
     */
    public function setRefid(Reference $r)
    {
        if ((count($this->parameters) !== 0) || ($this->className !== null)) {
            throw $this->tooManyAttributes();
        }
        $o = $r->getReferencedObject($this->getProject());
        if ($o instanceof PhingFilterReader) {
            $this->setClassName($o->getClassName());
            $this->setClasspath($o->getClassPath());
            foreach ($o->getParams() as $p) {
                $this->addParam($p);
            }
        } else {
            $msg = $r->getRefId() . " doesn\'t refer to a PhingFilterReader";
            throw new BuildException($msg);
        }

        parent::setRefid($r);
    }
}
<?php
/*
 * $Id: 6eaad613a6672e5101d55b4b880779ffc58540b8 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/** Class to hold a property value
 *  Class only required to make it possible to add a property as reference
 * @package phing.types
 */
class PropertyValue
{

    /**
     * @var string
     */
    protected $value;

    /**
     * Constructor optionaly sets a the value of property component.
     * @param    mixed      Value of name, all scalars allowed
     */
    public function __construct($value = null)
    {
        if ($value !== null) {
            $this->setValue($value);
        }
    }

    /**
     * Sets a the value of property component.
     * @param    mixed      Value of name, all scalars allowed
     */
    public function setValue($value)
    {
        $this->value = (string) $value;
    }

    /** Get the value of property component. */
    public function getValue()
    {
        return $this->value;
    }

    /**
     * @return string
     */
    public function toString()
    {
        return $this->getValue();
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Class to hold a reference to another object in the project.
 *
 * @package phing.types
 */
class Reference
{
    /** @var string $refid */
    protected $refid;

    /**
     * @param string $id
     */
    public function __construct($id = null)
    {
        if ($id !== null) {
            $this->setRefId($id);
        }
    }

    /**
     * @param $id
     */
    public function setRefId($id)
    {
        $this->refid = (string) $id;
    }

    /**
     * @return string
     */
    public function getRefId()
    {
        return $this->refid;
    }

    /**
     * returns reference to object in references container of project
     *
     * @param Project $project
     *
     * @throws BuildException
     *
     * @return Reference
     */
    public function getReferencedObject(Project $project)
    {
        if ($this->refid === null) {
            throw new BuildException("No reference specified");
        }
        $refs = $project->getReferences();
        $o = @$refs[$this->refid];
        if (!is_object($o)) {
            throw new BuildException("Reference {$this->refid} not found.");
        }

        return $o;
    }
}
<?php
/*
 *  $Id: 4b0da5e4e84cc48bb4e9c669c305448d3d59b11a $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

include_once 'phing/types/DataType.php';
include_once 'phing/Project.php';
include_once 'phing/util/regexp/Regexp.php';

/**
 * A regular expression datatype.  Keeps an instance of the
 * compiled expression for speed purposes.  This compiled
 * expression is lazily evaluated (it is compiled the first
 * time it is needed).  The syntax is the dependent on which
 * regular expression type you are using.
 *
 * @author    <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a>
 * @version   $Id: 4b0da5e4e84cc48bb4e9c669c305448d3d59b11a $
 * @see       phing.util.regex.RegexMatcher
 * @package   phing.types
 */
class RegularExpression extends DataType
{

    private $regexp = null;
    /**
     * @todo Probably both $ignoreCase and $multiline should be removed
     * from attribute list of RegularExpression class:
     * actual values are preserved on regexp *engine* level, not expression
     * object itself.
     */
    private $ignoreCase = false;
    private $multiline = false;

    /**
     *
     */
    public function __construct()
    {
        $this->regexp = new Regexp();
    }

    /**
     * @param $pattern
     */
    public function setPattern($pattern)
    {
        $this->regexp->setPattern($pattern);
    }

    /**
     * @param $replace
     */
    public function setReplace($replace)
    {
        $this->regexp->setReplace($replace);
    }

    /**
     * @param $p
     * @return string
     * @throws BuildException
     */
    public function getPattern($p)
    {
        if ($this->isReference()) {
            $ref = $this->getRef($p);

            return $ref->getPattern($p);
        }

        return $this->regexp->getPattern();
    }

    /**
     * @param Project $p
     * @return string
     * @throws BuildException
     */
    public function getReplace($p)
    {
        if ($this->isReference()) {
            $ref = $this->getRef($p);

            return $ref->getReplace($p);
        }

        return $this->regexp->getReplace();
    }

    /**
     * @param $modifiers
     */
    public function setModifiers($modifiers)
    {
        $this->regexp->setModifiers($modifiers);
    }

    /**
     * @return string
     */
    public function getModifiers()
    {
        return $this->regexp->getModifiers();
    }

    /**
     * @param $bit
     */
    public function setIgnoreCase($bit)
    {
        $this->regexp->setIgnoreCase($bit);
    }

    /**
     * @return bool
     */
    public function getIgnoreCase()
    {
        return $this->regexp->getIgnoreCase();
    }

    /**
     * @param $multiline
     */
    public function setMultiline($multiline)
    {
        $this->regexp->setMultiline($multiline);
    }

    /**
     * @return bool
     */
    public function getMultiline()
    {
        return $this->regexp->getMultiline();
    }

    /**
     * @param Project $p
     * @return null|Regexp
     * @throws BuildException
     */
    public function getRegexp(Project $p)
    {
        if ($this->isReference()) {
            $ref = $this->getRef($p);

            return $ref->getRegexp($p);
        }

        return $this->regexp;
    }

    /**
     * @param Project $p
     * @return mixed
     * @throws BuildException
     */
    public function getRef(Project $p)
    {
        if (!$this->checked) {
            $stk = array();
            array_push($stk, $this);
            $this->dieOnCircularReference($stk, $p);
        }

        $o = $this->ref->getReferencedObject($p);
        if (!($o instanceof RegularExpression)) {
            throw new BuildException($this->ref->getRefId() . " doesn't denote a RegularExpression");
        } else {
            return $o;
        }
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/selectors/SelectorContainer.php';
require_once 'phing/types/DataType.php';

/**
 * This is the base class for selectors that can contain other selectors.

 * @author Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.types.selectors
 */
abstract class AbstractSelectorContainer extends DataType implements SelectorContainer
{

    private $selectorsList = array();

    /**
     * Performs the check for circular references and returns the
     * referenced FileSet.
     *
     * @param Project $p
     *
     * @throws BuildException
     *
     * @return FileSet
     */
    public function getRef(Project $p)
    {
        if (!$this->checked) {
            $stk = array();
            array_push($stk, $this);
            $this->dieOnCircularReference($stk, $p);
        }

        $o = $this->ref->getReferencedObject($p);
        if (!($o instanceof FileSet)) {
            $msg = $this->ref->getRefId() . " doesn't denote a fileset";
            throw new BuildException($msg);
        } else {
            return $o;
        }
    }

    /**
     * Indicates whether there are any selectors here.
     *
     * @return boolean Whether any selectors are in this container
     */
    public function hasSelectors()
    {
        if ($this->isReference() && $this->getProject() !== null) {
            return $this->getRef($this->getProject())->hasSelectors();
        }

        return !empty($this->selectors);
    }

    /**
     * Convert the Selectors within this container to a string. This will
     * just be a helper class for the subclasses that put their own name
     * around the contents listed here.
     *
     * @return string comma separated list of Selectors contained in this one
     */
    public function __toString()
    {
        return implode(', ', $this->selectorElements());
    }

    /**
     * <p>
     * This validates each contained selector
     * provided that the selector implements the validate interface.
     * </p>
     * <p>Ordinarily, this will validate all the elements of a selector
     * container even if the isSelected() method of some elements is
     * never called. This has two effects:</p>
     * <ul>
     * <li>Validation will often occur twice.
     * <li>Since it is not required that selectors derive from
     * BaseSelector, there could be selectors in the container whose
     * error conditions are not detected if their isSelected() call
     * is never made.
     * </ul>
     */
    public function validate()
    {
        if ($this->isReference()) {
            $dataTypeName = StringHelper::substring(get_class(), strrpos(get_class(), '\\') + 1);
            $this->getCheckedRef(get_class(), $dataTypeName)->validate();
        }
        $selectorElements = $this->selectorElements();
        $this->dieOnCircularReference($selectorElements, $this->getProject());
        foreach ($selectorElements as $o) {
            if ($o instanceof BaseSelector) {
                $o->validate();
            }
        }
    }


    /**
     * Gives the count of the number of selectors in this container
     *
     * @throws Exception
     * @return int The number of selectors in this container
     */
    public function selectorCount()
    {
        if ($this->isReference() && $this->getProject() !== null) {
            try {
                return $this->getRef($this->getProject())->selectorCount();
            } catch (Exception $e) {
                throw $e;
            }
        }

        return count($this->selectorsList);
    }

    /**
     * Returns the set of selectors as an array.
     *
     * @param Project $p
     * @throws BuildException
     * @return array of selectors in this container
     */
    public function getSelectors(Project $p)
    {
        if ($this->isReference()) {
            return $this->getRef($p)->getSelectors($p);
        } else {
            // *copy* selectors
            $result = array();
            for ($i = 0, $size = count($this->selectorsList); $i < $size; $i++) {
                $result[] = clone $this->selectorsList[$i];
            }

            return $result;
        }
    }

    /**
     * Returns an array for accessing the set of selectors.
     *
     * @return array The array of selectors
     */
    public function selectorElements()
    {
        if ($this->isReference() && $this->getProject() !== null) {
            return $this->getRef($this->getProject())->selectorElements();
        }

        return $this->selectorsList;
    }

    /**
     * Add a new selector into this container.
     *
     * @param FileSelector $selector new selector to add
     *
     * @throws BuildException
     *
     * @return void
     */
    public function appendSelector(FileSelector $selector)
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }
        $this->selectorsList[] = $selector;
    }

    /* Methods below all add specific selectors */

    /**
     * add a "Select" selector entry on the selector list
     *
     * @return SelectSelector
     */
    public function createSelector()
    {
        $o = new SelectSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add an "And" selector entry on the selector list
     *
     * @return AndSelector
     */
    public function createAnd()
    {
        $o = new AndSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add an "Or" selector entry on the selector list
     *
     * @return OrSelector
     */
    public function createOr()
    {
        $o = new OrSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a "Not" selector entry on the selector list
     */
    public function createNot()
    {
        $o = new NotSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a "None" selector entry on the selector list
     */
    public function createNone()
    {
        $o = new NoneSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a majority selector entry on the selector list
     */
    public function createMajority()
    {
        $o = new MajoritySelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a selector date entry on the selector list
     */
    public function createDate()
    {
        $o = new DateSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a selector different entry on the selector list
     */
    public function createDifferent()
    {
        $o = new DifferentSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a selector size entry on the selector list
     */
    public function createSize()
    {
        $o = new SizeSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a selector filename entry on the selector list
     */
    public function createFilename()
    {
        $o = new FilenameSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add an extended selector entry on the selector list
     */
    public function createCustom()
    {
        $o = new ExtendSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a contains selector entry on the selector list
     */
    public function createContains()
    {
        $o = new ContainsSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a contains selector entry on the selector list
     */
    public function createContainsRegexp()
    {
        $o = new ContainsRegexpSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a present selector entry on the selector list
     */
    public function createPresent()
    {
        $o = new PresentSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a depth selector entry on the selector list
     */
    public function createDepth()
    {
        $o = new DepthSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a depends selector entry on the selector list
     */
    public function createDepend()
    {
        $o = new DependSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a type selector entry on the selector list
     */
    public function createType()
    {
        $o = new TypeSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a readable selector entry on the selector list
     */
    public function createReadable()
    {
        $o = new ReadableSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a writable selector entry on the selector list
     */
    public function createWritable()
    {
        $o = new WritableSelector();
        $this->appendSelector($o);

        return $o;
    }

    public function dieOnCircularReference(&$stk, Project $p)
    {
        if ($this->checked) {
            return;
        }

        if ($this->isReference()) {
            parent::dieOnCircularReference($stk, $p);
        } else {
            foreach ($this->selectorsList as $fileSelector) {
                if ($fileSelector instanceof DataType) {
                    self::pushAndInvokeCircularReferenceCheck($fileSelector, $stk, $p);
                }
            }
            $this->checked = true;
        }
    }
}
<?php
/*
 * $Id: a48248c2ea6319b26b375825aa437e5b1a9e7f78 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/selectors/BaseSelectorContainer.php';

/**
 * This selector has a collection of other selectors, all of which have to
 * select a file in order for this selector to select it.
 *
 * @author Hans Lellelid, hans@xmpl.org (Phing)
 * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a> (Ant)
 * @package phing.types.selectors
 */
class AndSelector extends BaseSelectorContainer
{

    /**
     * @return string
     */
    public function toString()
    {
        $buf = "";
        if ($this->hasSelectors()) {
            $buf .= "{andselect: ";
            $buf .= parent::toString();
            $buf .= "}";
        }

        return $buf;
    }

    /**
     * Returns true (the file is selected) only if all other selectors
     * agree that the file should be selected.
     *
     * @param PhingFile $basedir the base directory the scan is being done from
     * @param string $filename the name of the file to check
     * @param PhingFile $file a PhingFile object for the filename that the selector
     * can use
     * @return bool whether the file should be selected or not
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
    {
        $this->validate();
        $selectors = $this->selectorElements();
        for ($i = 0, $size = count($selectors); $i < $size; $i++) {
            $result = $selectors[$i]->isSelected($basedir, $filename, $file);
            if (!$result) {
                return false;
            }
        }

        return true;
    }

}
<?php

/*
 * $Id: 199994295e0eb776fdb3987075804074171abef4 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/selectors/ExtendFileSelector.php';
require_once 'phing/types/selectors/BaseSelector.php';
include_once 'phing/types/Parameter.php';

/**
 * Convenience base class for all selectors accessed through ExtendSelector.
 * It provides support for gathering the parameters together as well as for
 * assigning an error message and throwing a build exception if an error is
 * detected.
 *
 * @author Hans Lellelid, hans@xmpl.org (Phing)
 * @author Bruce Atherton, bruce@callenish.com (Ant)
 * @package phing.types.selectors
 */
abstract class BaseExtendSelector extends BaseSelector implements ExtendFileSelector
{

    /** The passed in parameter array. */
    protected $parameters = null;

    /**
     * Set all the Parameters for this custom selector, collected by
     * the ExtendSelector class.
     *
     * @param array $parameters the complete set of parameters for this selector
     * @return mixed|void
     */
    public function setParameters($parameters)
    {
        $this->parameters = $parameters;
    }

    /**
     * Allows access to the parameters gathered and set within the
     * &lt;custom&gt; tag.
     *
     * @return array the set of parameters defined for this selector
     */
    protected function getParameters()
    {
        return $this->parameters;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/selectors/FileSelector.php';

/**
 * A convenience base class that you can subclass Selectors from. It
 * provides some helpful common behaviour. Note that there is no need
 * for Selectors to inherit from this class, it is only necessary that
 * they implement FileSelector.
 *
 * {@inheritdoc}
 *
 * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a>
 *
 * @package phing.types.selectors
 */
abstract class BaseSelector extends DataType implements FileSelector
{
    /** @var string $errmsg */
    private $errmsg = null;

    /**
     * Allows all selectors to indicate a setup error. Note that only
     * the first error message is recorded.
     *
     * @param string $msg The error message any BuildException should throw.
     *
     * @return void
     */
    public function setError($msg)
    {
        if ($this->errmsg === null) {
            $this->errmsg = $msg;
        }
    }

    /**
     * Returns any error messages that have been set.
     *
     * @return string the error condition
     */
    public function getError()
    {
        return $this->errmsg;
    }

    /**
     * <p>Subclasses can override this method to provide checking of their
     * state. So long as they call validate() from isSelected(), this will
     * be called automatically (unless they override validate()).</p>
     * <p>Implementations should check for incorrect settings and call
     * setError() as necessary.</p>
     */
    public function verifySettings()
    {
    }

    /**
     * Subclasses can use this to throw the requisite exception
     * in isSelected() in the case of an error condition.
     *
     * @throws BuildException
     */
    public function validate()
    {
        if ($this->getError() === null) {
            $this->verifySettings();
        }
        if ($this->getError() !== null) {
            throw new BuildException($this->errmsg);
        }
    }
}
<?php

/*
 * $Id: d344784133b397d4e9b2e450de370ba39dc9bd69 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/selectors/SelectorContainer.php';
require_once 'phing/types/selectors/BaseSelector.php';

/**
 * This is the base class for selectors that can contain other selectors.
 *
 * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a> (Ant)
 * @package phing.types.selectors
 */
abstract class BaseSelectorContainer extends BaseSelector implements SelectorContainer
{

    private $selectorsList = array();

    /**
     * Indicates whether there are any selectors here.
     */
    public function hasSelectors()
    {
        return !(empty($this->selectorsList));
    }

    /**
     * Gives the count of the number of selectors in this container
     */
    public function selectorCount()
    {
        return count($this->selectorsList);
    }

    /**
     * Returns a copy of the selectors as an array.
     * @param Project $p
     * @return array
     */
    public function getSelectors(Project $p)
    {
        $result = array();
        for ($i = 0, $size = count($this->selectorsList); $i < $size; $i++) {
            $result[] = clone $this->selectorsList[$i];
        }

        return $result;
    }

    /**
     * Returns an array for accessing the set of selectors (not a copy).
     */
    public function selectorElements()
    {
        return $this->selectorsList;
    }

    /**
     * Convert the Selectors within this container to a string. This will
     * just be a helper class for the subclasses that put their own name
     * around the contents listed here.
     *
     * @return string comma separated list of Selectors contained in this one
     */
    public function toString()
    {
        $buf = "";
        $arr = $this->selectorElements();
        for ($i = 0, $size = count($arr); $i < $size; $i++) {
            $buf .= $arr[$i]->toString() . (isset($arr[$i + 1]) ? ', ' : '');
        }

        return $buf;
    }

    /**
     * Add a new selector into this container.
     *
     * @param FileSelector|the $selector
     * @internal param the $selector new selector to add
     * @return the selector that was added
     */
    public function appendSelector(FileSelector $selector)
    {
        $this->selectorsList[] = $selector;
    }

    /**
     * <p>This implementation validates the container by calling
     * verifySettings() and then validates each contained selector
     * provided that the selector implements the validate interface.
     * </p>
     * <p>Ordinarily, this will validate all the elements of a selector
     * container even if the isSelected() method of some elements is
     * never called. This has two effects:</p>
     * <ul>
     * <li>Validation will often occur twice.
     * <li>Since it is not required that selectors derive from
     * BaseSelector, there could be selectors in the container whose
     * error conditions are not detected if their isSelected() call
     * is never made.
     * </ul>
     */
    public function validate()
    {
        $this->verifySettings();
        $errmsg = $this->getError();
        if ($errmsg !== null) {
            throw new BuildException($errmsg);
        }
        foreach ($this->selectorsList as $o) {
            if ($o instanceof BaseSelector) {
                $o->validate();
            }
        }
    }

    /* Methods below all add specific selectors */

    /**
     * add a "Select" selector entry on the selector list
     */
    public function createSelector()
    {
        $o = new SelectSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add an "And" selector entry on the selector list
     */
    public function createAnd()
    {
        $o = new AndSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add an "Or" selector entry on the selector list
     */
    public function createOr()
    {
        $o = new OrSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a "Not" selector entry on the selector list
     */
    public function createNot()
    {
        $o = new NotSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a "None" selector entry on the selector list
     */
    public function createNone()
    {
        $o = new NoneSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a majority selector entry on the selector list
     */
    public function createMajority()
    {
        $o = new MajoritySelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a selector date entry on the selector list
     */
    public function createDate()
    {
        $o = new DateSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a selector size entry on the selector list
     */
    public function createSize()
    {
        $o = new SizeSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a selector filename entry on the selector list
     */
    public function createFilename()
    {
        $o = new FilenameSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add an extended selector entry on the selector list
     */
    public function createCustom()
    {
        $o = new ExtendSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a contains selector entry on the selector list
     */
    public function createContains()
    {
        $o = new ContainsSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a contains selector entry on the selector list
     */
    public function createContainsRegexp()
    {
        $o = new ContainsRegexpSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a present selector entry on the selector list
     */
    public function createPresent()
    {
        $o = new PresentSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a depth selector entry on the selector list
     */
    public function createDepth()
    {
        $o = new DepthSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a depends selector entry on the selector list
     */
    public function createDepend()
    {
        $o = new DependSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a different selector entry on the selector list
     */
    public function createDifferent()
    {
        $o = new DifferentSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a type selector entry on the selector list
     */
    public function createType()
    {
        $o = new TypeSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a readable selector entry on the selector list
     */
    public function createReadable()
    {
        $o = new ReadableSelector();
        $this->appendSelector($o);

        return $o;
    }

    /**
     * add a writable selector entry on the selector list
     */
    public function createWritable()
    {
        $o = new WritableSelector();
        $this->appendSelector($o);

        return $o;
    }
}
<?php

/*
 * $Id: 163cfcb942963e4734e2a32ab911915bf2dc6b78 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/selectors/BaseExtendSelector.php';
include_once 'phing/types/RegularExpression.php';

/**
 * Selector that filters files based on whether they contain a
 * particular string using regexp.
 *
 * @author    Hans Lellelid <hans@xmpl.org> (Phing)
 * @author    Bruce Atherton <bruce@callenish.com> (Ant)
 * @version   $Id: 163cfcb942963e4734e2a32ab911915bf2dc6b78 $
 * @package   phing.types.selectors
 */
class ContainsRegexpSelector extends BaseExtendSelector
{
    /**
     * The expression set from XML.
     *
     * @var string $userProvidedExpression
     */
    private $userProvidedExpression;

    /** @var Regexp $myExpression */
    private $myExpression;

    /** @var bool $casesensitive */
    private $casesensitive = true;

    /** @var RegularExpression $myRegExp */
    private $myRegExp;

    const EXPRESSION_KEY = "expression";
    const CASE_KEY = "casesensitive";

    /**
     * @return string
     */
    public function toString()
    {
        $buf = "{containsregexpselector expression: ";
        $buf .= $this->userProvidedExpression;
        $buf .= " casesensitive: ";
        if ($this->casesensitive) {
            $buf .= "true";
        } else {
            $buf .= "false";
        }
        $buf .= "}";

        return $buf;
    }

    /**
     * The expression to match on within a file.
     *
     * @param string $exp the string that a file must contain to be selected.
     */
    public function setExpression($exp)
    {
        $this->userProvidedExpression = $exp;
    }

    /**
     * Whether to ignore case in the regex match.
     *
     * @param boolean $casesensitive whether to pay attention to case sensitivity
     */
    public function setCasesensitive($casesensitive)
    {
        $this->casesensitive = $casesensitive;
    }

    /**
     * When using this as a custom selector, this method will be called.
     * It translates each parameter into the appropriate setXXX() call.
     *
     * @param array $parameters the complete set of parameters for this selector
     *
     * @return void
     */
    public function setParameters($parameters)
    {
        parent::setParameters($parameters);
        if ($parameters !== null) {
            for ($i = 0, $size = count($parameters); $i < $size; $i++) {
                $paramname = $parameters[$i]->getName();
                switch (strtolower($paramname)) {
                    case self::EXPRESSION_KEY:
                        $this->setExpression($parameters[$i]->getValue());
                        break;
                    case self::CASE_KEY:
                        $this->setCasesensitive($parameters[$i]->getValue());
                        break;
                    default:
                        $this->setError("Invalid parameter " . $paramname);
                }
            } // for each param
        } // if params
    }

    /**
     * Checks to make sure all settings are kosher. In this case, it
     * means that the pattern attribute has been set.
     *
     */
    public function verifySettings()
    {
        if ($this->userProvidedExpression === null) {
            $this->setError("The expression attribute is required");
        }
    }

    /**
     * The heart of the matter. This is where the selector gets to decide
     * on the inclusion of a file in a particular fileset.
     *
     * @param PhingFile $basedir base directory the scan is being done from
     * @param string $filename the name of the file to check
     * @param PhingFile $file PhingFile object the selector can use
     *
     * @throws BuildException
     *
     * @return bool whether the file should be selected or not
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
    {

        $this->validate();

        if ($file->isDirectory()) {
            return true;
        }

        if ($this->myRegExp === null) {
            $this->myRegExp = new RegularExpression();
            $this->myRegExp->setPattern($this->userProvidedExpression);
            if (!$this->casesensitive) {
                $this->myRegExp->setIgnoreCase(true);
            }
            $this->myExpression = $this->myRegExp->getRegexp($this->getProject());
        }

        $in = null;
        try {
            $in = new BufferedReader(new FileReader($file));
            $teststr = $in->readLine();
            while ($teststr !== null) {
                if ($this->myExpression->matches($teststr)) {
                    return true;
                }
                $teststr = $in->readLine();
            }

            $in->close();

            return false;
        } catch (IOException $ioe) {
            if ($in) {
                $in->close();
            }
            throw new BuildException("Could not read file " . $filename);
        }
    }

}
<?php

/*
 * $Id: 5d7d39fd94f07bb38f7bf01212ea7e845cb5858d $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/types/selectors/BaseExtendSelector.php';

/**
 * Selector that filters files based on whether they contain a
 * particular string.
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Bruce Atherton <bruce@callenish.com> (Ant)
 * @package phing.types.selectors
 */
class ContainsSelector extends BaseExtendSelector
{

    private $contains = null;
    private $casesensitive = true;
    const CONTAINS_KEY = "text";
    const CASE_KEY = "casesensitive";
    const WHITESPACE_KEY = "ignorewhitespace";
    private $ignorewhitespace = false;

    /**
     * @return string
     */
    public function toString()
    {
        $buf = "{containsselector text: ";
        $buf .= $this->contains;
        $buf .= " casesensitive: ";
        if ($this->casesensitive) {
            $buf .= "true";
        } else {
            $buf .= "false";
        }
        $buf .= " ignorewhitespace: ";
        if ($this->ignorewhitespace) {
            $buf .= "true";
        } else {
            $buf .= "false";
        }
        $buf .= "}";

        return $buf;
    }

    /**
     * The string to search for within a file.
     *
     * @param string $contains the string that a file must contain to be selected.
     */
    public function setText($contains)
    {
        $this->contains = $contains;
    }

    /**
     * Whether to ignore case in the string being searched.
     *
     * @param boolean $casesensitive whether to pay attention to case sensitivity
     */
    public function setCasesensitive($casesensitive)
    {
        $this->casesensitive = $casesensitive;
    }

    /**
     * @param boolean $ignoreWhitespace
     */
    public function setIgnoreWhitespace($ignoreWhitespace)
    {
        $this->ignorewhitespace = $ignoreWhitespace;
    }

    /**
     * When using this as a custom selector, this method will be called.
     * It translates each parameter into the appropriate setXXX() call.
     *
     * @param array $parameters the complete set of parameters for this selector
     * @return mixed|void
     */
    public function setParameters($parameters)
    {
        parent::setParameters($parameters);
        if ($parameters !== null) {
            for ($i = 0, $size = count($parameters); $i < $size; $i++) {
                $paramname = $parameters[$i]->getName();
                switch (strtolower($paramname)) {
                    case self::CONTAINS_KEY:
                        $this->setText($parameters[$i]->getValue());
                        break;
                    case self::CASE_KEY:
                        $this->setCasesensitive($parameters[$i]->getValue());
                        break;
                    case self::WHITESPACE_KEY:
                        $this->setIgnoreWhitespace($parameters[$i]->getValue());
                        break;
                    default:
                        $this->setError("Invalid parameter " . $paramname);
                }
            } // for each param
        } // if params
    }

    /**
     * Checks to make sure all settings are kosher. In this case, it
     * means that the pattern attribute has been set.
     *
     */
    public function verifySettings()
    {
        if ($this->contains === null) {
            $this->setError("The text attribute is required");
        }
    }

    /**
     * The heart of the matter. This is where the selector gets to decide
     * on the inclusion of a file in a particular fileset.
     *
     * @param PhingFile $basedir
     * @param string $filename
     * @param PhingFile $file
     *
     * @throws BuildException
     *
     * @internal param the $basedir base directory the scan is being done from
     * @internal param is $filename the name of the file to check
     * @internal param a $file PhingFile object the selector can use
     *
     * @return bool whether the file should be selected or not
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
    {

        $this->validate();

        if ($file->isDirectory()) {
            return true;
        }

        $userstr = $this->contains;
        if (!$this->casesensitive) {
            $userstr = strtolower($this->contains);
        }
        if ($this->ignorewhitespace) {
            $userstr = SelectorUtils::removeWhitespace($userstr);
        }

        $in = null;
        try {
            $in = new BufferedReader(new FileReader($file));
            $teststr = $in->readLine();
            while ($teststr !== null) {
                if (!$this->casesensitive) {
                    $teststr = strtolower($teststr);
                }
                if ($this->ignorewhitespace) {
                    $teststr = SelectorUtils::removeWhitespace($teststr);
                }
                if (strpos($teststr, $userstr) !== false) {
                    return true;
                }
                $teststr = $in->readLine();
            }

            $in->close();

            return false;
        } catch (IOException $ioe) {
            if ($in) {
                $in->close();
            }
            throw new BuildException("Could not read file " . $filename);
        }
    }
}
<?php

/*
 * $Id: 798670a89403d9fde4ef287b86e49d22bfd5e263 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/selectors/BaseExtendSelector.php';

/**
 * Selector that chooses files based on their last modified date. Ant uses
 * millisecond precision (thanks to Java); PHP is forced to use only seconds
 * precision.
 *
 * @author    Hans Lellelid <hans@xmpl.org> (Phing)
 * @author    Bruce Atherton <bruce@callenish.com> (Ant)
 * @version   $Id: 798670a89403d9fde4ef287b86e49d22bfd5e263 $
 * @package   phing.types.selectors
 */
class DateSelector extends BaseExtendSelector
{

    private $seconds = -1; // millis in Ant, but PHP doesn't support that level of precision
    private $dateTime = null;
    private $includeDirs = false;
    private $granularity = 0;
    private $cmp = 2;
    const MILLIS_KEY = "millis";
    const DATETIME_KEY = "datetime";
    const CHECKDIRS_KEY = "checkdirs";
    const GRANULARITY_KEY = "granularity";
    const WHEN_KEY = "when";
    private static $timeComparisons = array("before", "after", "equal");

    /**
     *
     */
    public function __construct()
    {
        //if (Os.isFamily("dos")) {
        //    granularity = 2000;
        //}
    }

    /**
     * @return string
     */
    public function toString()
    {
        $buf = "{dateselector date: ";
        $buf .= $this->dateTime;
        $buf .= " compare: ";
        if ($this->cmp === 0) {
            $buf .= "before";
        } elseif ($this->cmp === 1) {
            $buf .= "after";
        } else {
            $buf .= "equal";
        }
        $buf .= " granularity: ";
        $buf .= $this->granularity;
        $buf .= "}";

        return $buf;
    }

    /**
     * For users that prefer to express time in seconds since 1970
     *
     * @param int $seconds the time to compare file's last modified date to,
     *                     expressed in milliseconds
     */
    public function setSeconds($seconds)
    {
        $this->seconds = (int) $seconds;
    }

    /**
     * Returns the seconds value the selector is set for.
     */
    public function getSeconds()
    {
        return $this->seconds;
    }

    /**
     * Sets the date. The user must supply it in MM/DD/YYYY HH:MM AM_PM
     * format
     *
     * @param string $dateTime a string in MM/DD/YYYY HH:MM AM_PM format
     */
    public function setDatetime($dateTime)
    {
        $dt = strtotime($dateTime);
        if ($dt == -1) {
            $this->setError(
                "Date of " . $dateTime
                . " Cannot be parsed correctly. It should be in"
                . " a format parsable by PHP's strtotime() function."
            );
        } else {
            $this->dateTime = $dateTime;
            $this->setSeconds($dt);
        }
    }

    /**
     * Should we be checking dates on directories?
     *
     * @param boolean $includeDirs whether to check the timestamp on directories
     */
    public function setCheckdirs($includeDirs)
    {
        $this->includeDirs = (boolean) $includeDirs;
    }

    /**
     * Sets the number of milliseconds leeway we will give before we consider
     * a file not to have matched a date.
     * @param int $granularity
     */
    public function setGranularity($granularity)
    {
        $this->granularity = (int) $granularity;
    }

    /**
     * Sets the type of comparison to be done on the file's last modified
     * date.
     *
     * @param string $cmp The comparison to perform
     */
    public function setWhen($cmp)
    {
        $idx = array_search($cmp, self::$timeComparisons, true);
        if ($idx === null) {
            $this->setError("Invalid value for " . self::WHEN_KEY . ": " . $cmp);
        } else {
            $this->cmp = $idx;
        }
    }

    /**
     * When using this as a custom selector, this method will be called.
     * It translates each parameter into the appropriate setXXX() call.
     *
     * @param array $parameters the complete set of parameters for this selector
     * @return mixed|void
     */
    public function setParameters($parameters)
    {
        parent::setParameters($parameters);
        if ($parameters !== null) {
            for ($i = 0, $size = count($parameters); $i < $size; $i++) {
                $paramname = $parameters[$i]->getName();
                switch (strtolower($paramname)) {
                    case self::MILLIS_KEY:
                        $this->setMillis($parameters[$i]->getValue());
                        break;
                    case self::DATETIME_KEY:
                        $this->setDatetime($parameters[$i]->getValue());
                        break;
                    case self::CHECKDIRS_KEY:
                        $this->setCheckdirs($parameters[$i]->getValue());
                        break;
                    case self::GRANULARITY_KEY:
                        $this->setGranularity($parameters[$i]->getValue());
                        break;
                    case self::WHEN_KEY:
                        $this->setWhen($parameters[$i]->getValue());
                        break;
                    default:
                        $this->setError("Invalid parameter " . $paramname);
                } // switch
            }
        }
    }

    /**
     * This is a consistency check to ensure the selector's required
     * values have been set.
     */
    public function verifySettings()
    {
        if ($this->dateTime === null && $this->seconds < 0) {
            $this->setError(
                "You must provide a datetime or the number of "
                . "seconds."
            );
        } elseif ($this->seconds < 0) {
            $this->setError(
                "Date of " . $this->dateTime
                . " results in negative seconds"
                . " value relative to epoch (January 1, 1970, 00:00:00 GMT)."
            );
        }
    }

    /**
     * The heart of the matter. This is where the selector gets to decide
     * on the inclusion of a file in a particular fileset.
     *
     * @param  PhingFile $basedir  the base directory the scan is being done from
     * @param  string    $filename is the name of the file to check
     * @param  PhingFile $file     is a PhingFile object the selector can use
     * @return boolean   Whether the file should be selected or not
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
    {
        $this->validate();
        if ($file->isDirectory() && ($this->includeDirs === false)) {
            return true;
        }
        if ($this->cmp === 0) {
            return (($file->lastModified() - $this->granularity) < $this->seconds);
        } elseif ($this->cmp === 1) {
            return (($file->lastModified() - $this->granularity) > $this->seconds);
        } else {
            return (abs($file->lastModified() - $this->seconds) <= $this->granularity);
        }
    }

}
<?php

/*
 * $Id: 443e4a1ad090c819a7a6060a7f791d2d0d6e4469 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/selectors/BaseSelector.php';

/**
 * Selector that filters files based on whether they are newer than
 * a matching file in another directory tree. It can contain a mapper
 * element, so isn't available as an ExtendSelector (since those
 * parameters can't hold other elements).
 *
 * @author    Hans Lellelid <hans@xmpl.org> (Phing)
 * @author    Bruce Atherton <bruce@callenish.com> (Ant)
 * @version   $Id: 443e4a1ad090c819a7a6060a7f791d2d0d6e4469 $
 * @package   phing.types.selectors
 */
class DependSelector extends BaseSelector
{

    private $targetdir = null;
    private $mapperElement = null;
    private $map = null;
    private $granularity = 0;

    /**
     *
     */
    public function __construct()
    {
        // not yet supported:
        //if (Os.isFamily("dos")) {
        //    $this->granularity = 2000;
        //}
    }

    /**
     * @return string
     */
    public function toString()
    {
        $buf = "{dependselector targetdir: ";
        if ($this->targetdir === null) {
            $buf .= "NOT YET SET";
        } else {
            $buf .= $this->targetdir->getName();
        }
        $buf .= " granularity: ";
        $buf .= $this->granularity;
        if ($this->map !== null) {
            $buf .= " mapper: ";
            $buf .= $this->map->toString();
        } elseif ($this->mapperElement !== null) {
            $buf .= " mapper: ";
            $buf .= $this->mapperElement->toString();
        }
        $buf .= "}";

        return $buf;
    }

    /**
     * The name of the file or directory which is checked for out-of-date
     * files.
     *
     * @param PhingFile|the $targetdir
     * @internal param the $targetdir directory to scan looking for files.
     */
    public function setTargetdir(PhingFile $targetdir)
    {
        $this->targetdir = $targetdir;
    }

    /**
     * Sets the number of milliseconds leeway we will give before we consider
     * a file out of date.
     * @param $granularity
     */
    public function setGranularity($granularity)
    {
        $this->granularity = (int) $granularity;
    }

    /**
     * Defines the FileNameMapper to use (nested mapper element).
     * @throws BuildException
     */
    public function createMapper()
    {
        if ($this->mapperElement !== null) {
            throw new BuildException("Cannot define more than one mapper");
        }
        $this->mapperElement = new Mapper($this->project);

        return $this->mapperElement;
    }

    /**
     * Checks to make sure all settings are kosher. In this case, it
     * means that the dest attribute has been set and we have a mapper.
     */
    public function verifySettings()
    {
        if ($this->targetdir === null) {
            $this->setError("The targetdir attribute is required.");
        }
        if ($this->mapperElement === null) {
            $this->map = new IdentityMapper();
        } else {
            $this->map = $this->mapperElement->getImplementation();
        }
        if ($this->map === null) {
            $this->setError("Could not set <mapper> element.");
        }
    }

    /**
     * The heart of the matter. This is where the selector gets to decide
     * on the inclusion of a file in a particular fileset.
     *
     * @param PhingFile $basedir base directory the scan is being done from
     * @param string $filename the name of the file to check
     * @param PhingFile $file a PhingFile object the selector can use
     *
     * @throws BuildException
     *
     * @return bool whether the file should be selected or not
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
    {
        $this->validate();

        // Determine file whose out-of-dateness is to be checked
        $destfiles = $this->map->main($filename);

        // If filename does not match the To attribute of the mapper
        // then filter it out of the files we are considering
        if ($destfiles === null) {
            return false;
        }
        // Sanity check
        if (count($destfiles) !== 1 || $destfiles[0] === null) {
            throw new BuildException("Invalid destination file results for " . $this->targetdir . " with filename " . $filename);
        }
        $destname = $destfiles[0];
        $destfile = new PhingFile($this->targetdir, $destname);

        return SelectorUtils::isOutOfDate($file, $destfile, $this->granularity);
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/selectors/BaseExtendSelector.php';

/**
 * Selector that filters files based on the how deep in the directory
 * tree they are.
 *
 * @author    Hans Lellelid <hans@xmpl.org> (Phing)
 * @author    Bruce Atherton <bruce@callenish.com> (Ant)
 *
 * @package   phing.types.selectors
 */
class DepthSelector extends BaseExtendSelector
{
    /** @var int $min */
    public $min = -1;

    /** @var int $max */
    public $max = -1;

    const MIN_KEY = "min";
    const MAX_KEY = "max";

    /**
     * @return string
     */
    public function toString()
    {
        $buf = "{depthselector min: ";
        $buf .= $this->min;
        $buf .= " max: ";
        $buf .= $this->max;
        $buf .= "}";

        return $buf;
    }

    /**
     * The minimum depth below the basedir before a file is selected.
     *
     * @param int $min minimum directory levels below basedir to go
     *
     * @return void
     */
    public function setMin($min)
    {
        $this->min = (int) $min;
    }

    /**
     * The minimum depth below the basedir before a file is selected.
     *
     * @param int $max maximum directory levels below basedir to go
     *
     * @return void
     */
    public function setMax($max)
    {
        $this->max = (int) $max;
    }

    /**
     * When using this as a custom selector, this method will be called.
     * It translates each parameter into the appropriate setXXX() call.
     *
     * {@inheritdoc}
     *
     * @param array $parameters the complete set of parameters for this selector
     *
     * @return mixed|void
     */
    public function setParameters($parameters)
    {
        parent::setParameters($parameters);
        if ($parameters !== null) {
            for ($i = 0, $size = count($parameters); $i < $size; $i++) {
                $paramname = $parameters[$i]->getName();
                switch (strtolower($paramname)) {
                    case self::MIN_KEY:
                        $this->setMin($parameters[$i]->getValue());
                        break;
                    case self::MAX_KEY:
                        $this->setMax($parameters[$i]->getValue());
                        break;

                    default:
                        $this->setError("Invalid parameter " . $paramname);
                } // switch
            }
        }
    }

    /**
     * Checks to make sure all settings are kosher. In this case, it
     * means that the max depth is not lower than the min depth.
     *
     * {@inheritdoc}
     *
     * @return void
     */
    public function verifySettings()
    {
        if ($this->min < 0 && $this->max < 0) {
            $this->setError(
                "You must set at least one of the min or the " .
                "max levels."
            );
        }
        if ($this->max < $this->min && $this->max > -1) {
            $this->setError("The maximum depth is lower than the minimum.");
        }
    }

    /**
     * The heart of the matter. This is where the selector gets to decide
     * on the inclusion of a file in a particular fileset. Most of the work
     * for this selector is offloaded into SelectorUtils, a static class
     * that provides the same services for both FilenameSelector and
     * DirectoryScanner.
     *
     * {@inheritdoc}
     *
     * @param PhingFile $basedir base directory the scan is being done from
     * @param string $filename the name of the file to check
     * @param PhingFile $file a PhingFile object the selector can use
     *
     * @throws BuildException
     *
     * @return bool whether the file should be selected or not
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
    {

        $this->validate();

        $depth = -1;
        // If you felt daring, you could cache the basedir absolute path
        $abs_base = $basedir->getAbsolutePath();
        $abs_file = $file->getAbsolutePath();

        $tok_base = explode(DIRECTORY_SEPARATOR, $abs_base);
        $tok_file = explode(DIRECTORY_SEPARATOR, $abs_file);

        for ($i = 0, $size = count($tok_file); $i < $size; $i++) {
            $filetoken = $tok_file[$i];
            if (isset($tok_base[$i])) {
                $basetoken = $tok_base[$i];
                // Sanity check. Ditch it if you want faster performance
                if ($basetoken !== $filetoken) {
                    throw new BuildException("File " . $filename .
                        " does not appear within " . $abs_base . "directory");
                }
            } else { // no more basepath tokens
                $depth++;
                if ($this->max > -1 && $depth > $this->max) {
                    return false;
                }
            }
        }
        if (isset($tok_base[$i + 1])) {
            throw new BuildException("File " . $filename .
                " is outside of " . $abs_base . "directory tree");
        }
        if ($this->min > -1 && $depth < $this->min) {
            return false;
        }

        return true;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/selectors/MappingSelector.php';

/**
 * This selector selects files against a mapped set of target files, selecting
 * all those files which are different.
 * Files with different lengths are deemed different
 * automatically
 * Files with identical timestamps are viewed as matching by
 * default, unless you specify otherwise.
 * Contents are compared if the lengths are the same
 * and the timestamps are ignored or the same,
 * except if you decide to ignore contents to gain speed.
 * <p>
 * This is a useful selector to work with programs and tasks that don't handle
 * dependency checking properly; Even if a predecessor task always creates its
 * output files, followup tasks can be driven off copies made with a different
 * selector, so their dependencies are driven on the absolute state of the
 * files, not a timestamp.
 * <p>
 * Clearly, however, bulk file comparisons is inefficient; anything that can
 * use timestamps is to be preferred. If this selector must be used, use it
 * over as few files as possible, perhaps following it with an &lt;uptodate;&gt
 * to keep the descendant routines conditional.
 *
 */
class DifferentSelector extends MappingSelector
{
    /** @var bool $ignoreFileTimes */
    private $ignoreFileTimes = true;

    /** @var bool $ignoreContents */
    private $ignoreContents = false;

    /**
     * This flag tells the selector to ignore file times in the comparison
     * @param bool $ignoreFileTimes if true ignore file times
     */
    public function setIgnoreFileTimes($ignoreFileTimes)
    {
        $this->ignoreFileTimes = $ignoreFileTimes;
    }

    /**
     * This flag tells the selector to ignore contents
     * @param bool $ignoreContents if true ignore contents
     */
    public function setIgnoreContents($ignoreContents)
    {
        $this->ignoreContents = $ignoreContents;
    }

    /**
     * This test is our selection test that compared the file with the destfile.
     *
     * @param PhingFile $srcfile the source file
     * @param PhingFile $destfile the destination file
     * @return bool true if the files are different
     *
     * @throws BuildException
     */
    protected function selectionTest(PhingFile $srcfile, PhingFile $destfile)
    {
        try {
            // if either of them is missing, they are different
            if ($srcfile->exists() !== $destfile->exists()) {
                return true;
            }

            if ($srcfile->length() !== $destfile->length()) {
                // different size => different files
                return true;
            }

            if (!$this->ignoreFileTimes) {
                // different dates => different files
                if ($destfile->lastModified() !== $srcfile->lastModified()) {
                    return true;
                }
            }

            if (!$this->ignoreContents) {
                //here do a bulk comparison
                $fu = new FileUtils();

                return !$fu->contentEquals($srcfile, $destfile);
            }
        } catch (IOException $e) {
            throw new BuildException("while comparing $srcfile and $destfile", $e);
        }

        return false;
    }
}
<?php

/*
 * $Id: 3b9bc23a59e95799a682b7e868b6ac384e498107 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/Parameterizable.php';
require_once 'phing/types/selectors/FileSelector.php';

/**
 * This is the interface to be used by all custom selectors, those that are
 * called through the &lt;custom&gt; tag. It is the amalgamation of two
 * interfaces, the FileSelector and the Parameterizable interface. Note that
 * you will almost certainly want the default behaviour for handling
 * Parameters, so you probably want to use the BaseExtendSelector class
 * as the base class for your custom selector rather than implementing
 * this interface from scratch.
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Bruce Atherton <bruce@callenish.com> (Ant)
 * @package phing.types.selectors
 */
interface ExtendFileSelector extends Parameterizable, FileSelector
{
    // No further methods necessary. This is just an amalgamation of two other
    // interfaces.
}
<?php

/*
 * $Id: 50fe104e0e864bee411dd746e332279173d215fd $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/util/StringHelper.php';

/**
 * Selector that selects files by forwarding the request on to other classes.
 *
 * TODO - Consider adding Path (phing.types.Path) support to this class
 *         and to the Mappers class.  See Ant versions for implimentation details.
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Bruce Atherton <bruce@callenish.com> (Ant)
 * @package phing.types.selectors
 */
class ExtendSelector extends BaseSelector
{

    private $classname;
    private $dynselector;
    private $parameters = array();

    /**
     * Sets the classname of the custom selector.
     *
     * @param string $classname is the class which implements this selector
     */
    public function setClassname($classname)
    {
        $this->classname = $classname;
    }

    /**
     * Instantiates the identified custom selector class.
     */
    public function selectorCreate()
    {
        if ($this->classname !== null && $this->classname !== "") {
            try {
                // assume it's fully qualified, import it
                $cls = Phing::import($this->classname);

                // make sure class exists
                if (class_exists($cls)) {
                    $this->dynselector = new $cls();
                } else {
                    $this->setError("Selector " . $this->classname . " not initialized, no such class");
                }
            } catch (Exception $e) {
                $this->setError(
                    "Selector " . $this->classname . " not initialized, could not create class: " . $e->getMessage()
                );
            }
        } else {
            $this->setError("There is no classname specified");
        }
    }

    /**
     * Create new parameters to pass to custom selector.
     *
     * @param Parameter $p The new Parameter object
     * @return void
     */
    public function addParam(Parameter $p)
    {
        $this->parameters[] = $p;
    }

    /**
     * These are errors specific to ExtendSelector only. If there are
     * errors in the custom selector, it should throw a BuildException
     * when isSelected() is called.
     */
    public function verifySettings()
    {
        // Creation is done here rather than in isSelected() because some
        // containers may do a validation pass before running isSelected(),
        // but we need to check for the existence of the created class.
        if ($this->dynselector === null) {
            $this->selectorCreate();
        }

        if (empty($this->classname)) {
            $this->setError("The classname attribute is required");
        } elseif ($this->dynselector === null) {
            $this->setError("Internal Error: The custom selector was not created");
        } elseif (!($this->dynselector instanceof ExtendFileSelector) && (count($this->parameters) > 0)) {
            $this->setError(
                "Cannot set parameters on custom selector that does not "
                . "implement ExtendFileSelector."
            );
        }
    }

    /**
     * Allows the custom selector to choose whether to select a file. This
     * is also where the Parameters are passed to the custom selector.
     *
     * @param PhingFile $basedir
     * @param string $filename The filename
     * @param PhingFile $file
     * @return \whether
     * @throws BuildException
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
    {

        $this->validate();

        if (count($this->parameters) > 0 && $this->dynselector instanceof ExtendFileSelector) {
            // We know that dynselector must be non-null if no error message
            $this->dynselector->setParameters($this->parameters);
        }

        return $this->dynselector->isSelected($basedir, $filename, $file);
    }

}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/types/selectors/BaseExtendSelector.php';

/**
 * Selector that filters files based on the filename.
 *
 * @author Hans Lellelid, hans@xmpl.org (Phing)
 * @author Bruce Atherton, bruce@callenish.com (Ant)
 *
 * @package phing.types.selectors
 */
class FilenameSelector extends BaseExtendSelector
{

    private $pattern = null;
    private $casesensitive = true;
    private $negated = false;
    const NAME_KEY = "name";
    const CASE_KEY = "casesensitive";
    const NEGATE_KEY = "negate";

    /**
     * @return string
     */
    public function toString()
    {
        $buf = "{filenameselector name: ";
        $buf .= $this->pattern;
        $buf .= " negate: ";
        if ($this->negated) {
            $buf .= "true";
        } else {
            $buf .= "false";
        }
        $buf .= " casesensitive: ";
        if ($this->casesensitive) {
            $buf .= "true";
        } else {
            $buf .= "false";
        }
        $buf .= "}";

        return $buf;
    }

    /**
     * The name of the file, or the pattern for the name, that
     * should be used for selection.
     *
     * @param string $pattern the file pattern that any filename must match
     *                        against in order to be selected.
     *
     * @return void
     */
    public function setName($pattern)
    {
        $pattern = str_replace('\\', DIRECTORY_SEPARATOR, $pattern);
        $pattern = str_replace('/', DIRECTORY_SEPARATOR, $pattern);

        if (StringHelper::endsWith(DIRECTORY_SEPARATOR, $pattern)) {
            $pattern .= "**";
        }
        $this->pattern = $pattern;
    }

    /**
     * Whether to ignore case when checking filenames.
     *
     * @param bool $casesensitive whether to pay attention to case sensitivity
     *
     * @return void
     */
    public function setCasesensitive($casesensitive)
    {
        $this->casesensitive = $casesensitive;
    }

    /**
     * You can optionally reverse the selection of this selector,
     * thereby emulating an &lt;exclude&gt; tag, by setting the attribute
     * negate to true. This is identical to surrounding the selector
     * with &lt;not&gt;&lt;/not&gt;.
     *
     * @param bool $negated whether to negate this selection
     *
     * @return void
     */
    public function setNegate($negated)
    {
        $this->negated = $negated;
    }

    /**
     * When using this as a custom selector, this method will be called.
     * It translates each parameter into the appropriate setXXX() call.
     *
     * @param array $parameters the complete set of parameters for this selector
     *
     * @return void
     */
    public function setParameters($parameters)
    {
        parent::setParameters($parameters);
        if ($parameters !== null) {
            for ($i = 0, $len = count($parameters); $i < $len; $i++) {
                $paramname = $parameters[$i]->getName();
                switch (strtolower($paramname)) {
                    case self::NAME_KEY:
                        $this->setName($parameters[$i]->getValue());
                        break;
                    case self::CASE_KEY:
                        $this->setCasesensitive($parameters[$i]->getValue());
                        break;
                    case self::NEGATE_KEY:
                        $this->setNegate($parameters[$i]->getValue());
                        break;
                    default:
                        $this->setError("Invalid parameter " . $paramname);
                }
            } // for each param
        } // if params
    }

    /**
     * Checks to make sure all settings are kosher. In this case, it
     * means that the name attribute has been set.
     *
     * {@inheritdoc}
     *
     * @return void
     */
    public function verifySettings()
    {
        if ($this->pattern === null) {
            $this->setError("The name attribute is required");
        }
    }

    /**
     * The heart of the matter. This is where the selector gets to decide
     * on the inclusion of a file in a particular fileset. Most of the work
     * for this selector is offloaded into SelectorUtils, a static class
     * that provides the same services for both FilenameSelector and
     * DirectoryScanner.
     *
     * {@inheritdoc}
     *
     * @param PhingFile $basedir the base directory the scan is being done from
     * @param string $filename is the name of the file to check
     * @param PhingFile $file is a PhingFile object the selector can use
     *
     * @return bool whether the file should be selected or not
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
    {
        $this->validate();

        return (SelectorUtils::matchPath($this->pattern, $filename, $this->casesensitive)
            === !($this->negated));
    }
}
<?php

/*
 * $Id: 2d5d97b30ed0615591f57a2ff2b3346b09f902f5 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * This is the interface to be used by all selectors.
 *
 * @author Hans Lellelid, hans@xmpl.org (Phing)
 * @author Bruce Atherton, bruce@callenish.com (Ant)
 * @package phing.types.selectors
 */
interface FileSelector
{

    /**
     * Method that each selector will implement to create their
     * selection behaviour. If there is a problem with the setup
     * of a selector, it can throw a BuildException to indicate
     * the problem.
     *
     * @param PhingFile $basedir A PhingFile object for the base directory
     * @param string $filename The name of the file to check
     * @param PhingFile $file A PhingFile object for this filename
     * @return whether        the file should be selected or not
     * @throws BuildException if the selector was not configured correctly
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file);

}
<?php

/*
 * $Id: acddb2aa573b441a7b734797950b89d1a6ffed1a $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * This selector is here just to shake up your thinking a bit. Don't get
 * too caught up in boolean, there are other ways you can evaluate a
 * collection of selectors. This one takes a vote of the selectors it
 * contains, and majority wins. You could also have an "all-but-one"
 * selector, a "weighted-average" selector, and so on. These are left
 * as exercises for the reader (as are the usecases where this would
 * be necessary).
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Bruce Atherton <bruce@callenish.com> (Ant)
 * @package phing.types.selectors
 */
class MajoritySelector extends BaseSelectorContainer
{

    private $allowtie = true;

    /**
     * @return string
     */
    public function toString()
    {
        $buf = "";
        if ($this->hasSelectors()) {
            $buf .= "{majorityselect: ";
            $buf .= parent::toString();
            $buf .= "}";
        }

        return $buf;
    }

    /**
     * @param $tiebreaker
     */
    public function setAllowtie($tiebreaker)
    {
        $this->allowtie = $tiebreaker;
    }

    /**
     * Returns true (the file is selected) if most of the other selectors
     * agree. In case of a tie, go by the allowtie setting. That defaults
     * to true, meaning in case of a tie, the file is selected.
     *
     * @param PhingFile $basedir the base directory the scan is being done from
     * @param string $filename is the name of the file to check
     * @param PhingFile $file is a PhingFile object for the filename that the selector
     * can use
     * @return whether the file should be selected or not
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
    {

        $this->validate();

        $yesvotes = 0;
        $novotes = 0;

        $selectors = $this->selectorElements();
        for ($i = 0, $size = count($selectors); $i < $size; $i++) {
            $result = $selectors[$i]->isSelected($basedir, $filename, $file);
            if ($result) {
                $yesvotes = $yesvotes + 1;
            } else {
                $novotes = $novotes + 1;
            }
        }
        if ($yesvotes > $novotes) {
            return true;
        } else {
            if ($novotes > $yesvotes) {
                return false;
            }
        }
        // At this point, we know we have a tie.
        return $this->allowtie;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/selectors/BaseSelector.php';

/**
 * A mapping selector is an abstract class adding mapping support to the
 * base selector
 *
 * @author  Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.types.selectors
 */
abstract class MappingSelector extends BaseSelector
{
    /** @var PhingFile $targetdir */
    protected $targetdir;

    /** @var Mapper $mapperElement */
    protected $mapperElement;

    /** @var FileNameMapper $map */
    protected $map;

    /**
     * The name of the file or directory which is checked for out-of-date
     * files.
     *
     * @param PhingFile $targetdir the directory to scan looking for files.
     */
    public function setTargetdir(PhingFile $targetdir)
    {
        $this->targetdir = $targetdir;
    }

    /**
     * Defines the FileNameMapper to use (nested mapper element).
     * @return a mapper to be configured
     * @throws BuildException if more than one mapper defined
     */
    public function createMapper()
    {
        if ($this->map !== null || $this->mapperElement !== null) {
            throw new BuildException('Cannot define more than one mapper');
        }
        $this->mapperElement = new Mapper($this->getProject());
        return $this->mapperElement;
    }

    /**
     * Add a configured FileNameMapper instance.
     *
     * @param FileNameMapper $fileNameMapper the FileNameMapper to add
     *
     * @throws BuildException if more than one mapper defined
     */
    public function addConfigured(FileNameMapper $fileNameMapper)
    {
        if ($this->map !== null || $this->mapperElement !== null) {
            throw new BuildException('Cannot define more than one mapper');
        }
        $this->map = $fileNameMapper;
    }

    /**
     * Checks to make sure all settings are kosher. In this case, it
     * means that the dest attribute has been set and we have a mapper.
     */
    public function verifySettings()
    {
        if ($this->targetdir === null) {
            $this->setError("The targetdir attribute is required.");
        }
        if ($this->map === null) {
            if ($this->mapperElement === null) {
                $this->map = new IdentityMapper();
            } else {
                $this->map = $this->mapperElement->getImplementation();
                if ($this->map === null) {
                    $this->setError("Could not set <mapper> element.");
                }
            }
        }
    }

    /**
     * The heart of the matter. This is where the selector gets to decide
     * on the inclusion of a file in a particular fileset.
     *
     * @param PhingFile $basedir the base directory the scan is being done from
     * @param string $filename is the name of the file to check
     * @param PhingFile $file is a java.io.File object the selector can use
     *
     * @return bool whether the file should be selected or not
     *
     * @throws BuildException
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
    {

        // throw BuildException on error
        $this->validate();

        // Determine file whose out-of-dateness is to be checked
        $destfiles = $this->map->main($filename);
        // If filename does not match the To attribute of the mapper
        // then filter it out of the files we are considering
        if (empty($destfiles)) {
            return false;
        }
        // Sanity check
        if (count($destfiles) !== 1 || $destfiles[0] === null) {
            throw new BuildException('Invalid destination file results for '
                . $this->targetdir->getName() . ' with filename ' . $filename);
        }
        $destname = $destfiles[0];
        $fu = new FileUtils();
        $destfile = $fu->resolveFile($this->targetdir, $destname);

        return $this->selectionTest($file, $destfile);
    }

    /**
     * this test is our selection test that compared the file with the destfile
     * @param PhingFile $srcfile file to test; may be null
     * @param PhingFile $destfile destination file
     * @return true if source file compares with destination file
     */
    protected abstract function selectionTest(PhingFile $srcfile, PhingFile $destfile);
}
<?php
/*
 * $Id: fd4957709aa83a67caa6cc34ffafeeae062d0625 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/selectors/BaseSelectorContainer.php';

/**
 * This selector has a collection of other selectors. All of those selectors
 * must refuse to select a file before the file is considered selected by
 * this selector.
 *
 * @author Hans Lellelid <hans@xmpl.org>
 * @author Bruce Atherton <bruce@callenish.com> (Ant)
 * @package phing.types.selectors
 */
class NoneSelector extends BaseSelectorContainer
{

    /**
     * @return string
     */
    public function toString()
    {
        $buf = "";
        if ($this->hasSelectors()) {
            $buf .= "{noneselect: ";
            $buf .= parent::toString();
            $buf .= "}";
        }

        return $buf;
    }

    /**
     * Returns true (the file is selected) only if all other selectors
     * agree that the file should not be selected.
     *
     * @param PhingFile $basedir the base directory the scan is being done from
     * @param string $filename is the name of the file to check
     * @param PhingFile $file is a java.io.File object for the filename that the selector
     * can use
     * @return bool whether the file should be selected or not
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
    {

        $this->validate();

        $selectors = $this->selectorElements();

        for ($i = 0, $size = count($selectors); $i < $size; $i++) {
            $result = $selectors[$i]->isSelected($basedir, $filename, $file);
            if ($result) {
                return false;
            }
        }

        return true;
    }

}
<?php

/*
 * $Id: 11f5ebfbb271f1fc34e3f6e2795510f22306099e $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/selectors/NoneSelector.php';

/**
 * This selector has one other selectors whose meaning it inverts. It
 * actually relies on NoneSelector for its implementation of the
 * isSelected() method, but it adds a check to ensure there is only one
 * other selector contained within.
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Bruce Atherton <bruce@callenish.com> (Ant)
 * @package phing.types.selectors
 */
class NotSelector extends NoneSelector
{

    /**
     * @return string
     */
    public function toString()
    {
        $buf = "";
        if ($this->hasSelectors()) {
            $buf .= "{notselect: ";
            $buf .= parent::toString();
            $buf .= "}";
        }

        return $buf;
    }

    /**
     * Makes sure that there is only one entry, sets an error message if
     * not.
     */
    public function verifySettings()
    {
        if ($this->selectorCount() != 1) {
            $this->setError(
                "One and only one selector is allowed within the " .
                "<not> tag"
            );
        }
    }

}
<?php
/*
 * $Id: 807d3fc3d349dbf8c668bba761d7e0b8798fcca6 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/selectors/BaseSelectorContainer.php';

/**
 * This selector has a collection of other selectors, any of which have to
 * select a file in order for this selector to select it.
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Bruce Atherton <bruce@callenish.com> (Ant)
 * @package phing.types.selectors
 */
class OrSelector extends BaseSelectorContainer
{

    /**
     * @return string
     */
    public function toString()
    {
        $buf = "";
        if ($this->hasSelectors()) {
            $buf .= "{orselect: ";
            $buf .= parent::toString();
            $buf .= "}";
        }

        return $buf;
    }

    /**
     * Returns true (the file is selected) if any of the other selectors
     * agree that the file should be selected.
     *
     * @param PhingFile $basedir the base directory the scan is being done from
     * @param string filename the name of the file to check
     * @param PhingFile $file a PhingFile object for the filename that the selector
     * can use
     * @return boolean Whether the file should be selected or not
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
    {

        $this->validate();

        $selectors = $this->selectorElements();

        // First, check that all elements are correctly configured

        for ($i = 0, $size = count($selectors); $i < $size; $i++) {
            $result = $selectors[$i]->isSelected($basedir, $filename, $file);
            if ($result) {
                return true;
            }
        }

        return false;
    }

}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Selector that filters files based on whether they appear in another
 * directory tree. It can contain a mapper element, so isn't available
 * as an ExtendSelector (since those parameters can't hold other
 * elements).
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Bruce Atherton <bruce@callenish.com> (Ant)
 *
 * @package phing.types.selectors
 */
class PresentSelector extends BaseSelector
{
    private $targetdir = null;
    private $mapperElement = null;
    private $map = null;
    private $destmustexist = true;
    private static $filePresence = array("srconly", "both");

    /**
     * @return string
     */
    public function toString()
    {
        $buf = "{presentselector targetdir: ";
        if ($this->targetdir === null) {
            $buf .= "NOT YET SET";
        } else {
            $buf .= $this->targetdir->getName();
        }
        $buf .= " present: ";
        if ($this->destmustexist) {
            $buf .= "both";
        } else {
            $buf .= "srconly";
        }
        if ($this->map !== null) {
            $buf .= $this->map->toString();
        } elseif ($this->mapperElement !== null) {
            $buf .= $this->mapperElement->toString();
        }
        $buf .= "}";

        return $buf;
    }

    /**
     * The name of the file or directory which is checked for matching
     * files.
     *
     * @param PhingFile $targetdir the directory to scan looking for matching files.
     *
     * @return void
     */
    public function setTargetdir(PhingFile $targetdir)
    {
        $this->targetdir = $targetdir;
    }

    /**
     * Defines the FileNameMapper to use (nested mapper element).
     *
     * @return Mapper
     *
     * @throws BuildException
     */
    public function createMapper()
    {
        if ($this->mapperElement !== null) {
            throw new BuildException("Cannot define more than one mapper");
        }
        $this->mapperElement = new Mapper($this->getProject());

        return $this->mapperElement;
    }

    /**
     * This sets whether to select a file if its dest file is present.
     * It could be a <code>negate</code> boolean, but by doing things
     * this way, we get some documentation on how the system works.
     * A user looking at the documentation should clearly understand
     * that the ONLY files whose presence is being tested are those
     * that already exist in the source directory, hence the lack of
     * a <code>destonly</code> option.
     *
     * @param string $fp An attribute set to either <code>srconly</code> or
     *                   <code>both</code>.
     *
     * @return void
     */
    public function setPresent($fp)
    {
        $idx = array_search($fp, self::$filePresence, true);
        if ($idx === 0) {
            $this->destmustexist = false;
        }
    }

    /**
     * Checks to make sure all settings are kosher. In this case, it
     * means that the targetdir attribute has been set and we have a mapper.
     *
     * @return void
     */
    public function verifySettings()
    {
        if ($this->targetdir === null) {
            $this->setError("The targetdir attribute is required.");
        }
        if ($this->mapperElement === null) {
            $this->map = new IdentityMapper();
        } else {
            $this->map = $this->mapperElement->getImplementation();
        }
        if ($this->map === null) {
            $this->setError("Could not set <mapper> element.");
        }
    }

    /**
     * The heart of the matter. This is where the selector gets to decide
     * on the inclusion of a file in a particular fileset.
     *
     * @param PhingFile $basedir base directory the scan is being done from
     * @param string $filename the name of the file to check
     * @param PhingFile $file a PhingFile object the selector can use
     *
     * @throws BuildException
     *
     * @return bool whether the file should be selected or not
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
    {

        $this->validate();

        // Determine file whose existence is to be checked
        $destfiles = $this->map->main($filename);
        // If filename does not match the To attribute of the mapper
        // then filter it out of the files we are considering
        if ($destfiles === null) {
            return false;
        }
        // Sanity check
        if (count($destfiles) !== 1 || $destfiles[0] === null) {
            throw new BuildException("Invalid destination file results for "
                . $this->targetdir . " with filename " . $filename);
        }
        $destname = $destfiles[0];
        $destfile = new PhingFile($this->targetdir, $destname);

        return $destfile->exists() === $this->destmustexist;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/types/selectors/FileSelector.php';

/**
 * A selector that selects readable files.
 *
 * @author  Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.types.selectors
 */
class ReadableSelector implements FileSelector
{
    /**
     * {@inheritdoc}
     *
     * @param PhingFile $basedir
     * @param string $filename
     * @param PhingFile $file
     *
     * @return bool
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
    {
        return $file !== null && $file->canRead();
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * This is the interface for selectors that can contain other selectors.
 *
 * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a>
 *
 * @package phing.types.selectors
 */
interface SelectorContainer
{
    /**
     * Indicates whether there are any selectors here.
     *
     * @return bool whether any selectors are in this container
     */
    public function hasSelectors();

    /**
     * Gives the count of the number of selectors in this container
     *
     * @return int the number of selectors in this container
     */
    public function selectorCount();

    /**
     * Returns a *copy* of the set of selectors as an array.
     *
     * @param Project $p
     *
     * @return array an array of selectors in this container
     */
    public function getSelectors(Project $p);

    /**
     * Returns an array for accessing the set of selectors.
     *
     * @return an enumerator that goes through each of the selectors
     */
    public function selectorElements();

    /**
     * Add a new selector into this container.
     *
     * @param FileSelector $selector the new selector to add
     *
     * @return FileSelector the selector that was added
     */
    public function appendSelector(FileSelector $selector);

    /* Methods below all add specific selectors */

    /**
     * add a "Select" selector entry on the selector list
     */
    public function createSelector();

    /**
     * add an "And" selector entry on the selector list
     */
    public function createAnd();

    /**
     * add an "Or" selector entry on the selector list
     */
    public function createOr();

    /**
     * add a "Not" selector entry on the selector list
     */
    public function createNot();

    /**
     * add a "None" selector entry on the selector list
     */
    public function createNone();

    /**
     * add a majority selector entry on the selector list
     */
    public function createMajority();

    /**
     * add a selector date entry on the selector list
     */
    public function createDate();

    /**
     * add a selector size entry on the selector list
     */
    public function createSize();

    /**
     * add a selector filename entry on the selector list
     */
    public function createFilename();

    /**
     * add an extended selector entry on the selector list
     */
    public function createCustom();

    /**
     * add a contains selector entry on the selector list
     */
    public function createContains();

    /**
     * add a present selector entry on the selector list
     */
    public function createPresent();

    /**
     * add a depth selector entry on the selector list
     */
    public function createDepth();

    /**
     * add a depends selector entry on the selector list
     */
    public function createDepend();

    /**
     * add a different selector entry on the selector list
     */
    public function createDifferent();
}
<?php

/*
 * $Id: cdf80453207d82ddc2bb959a2a63939fdfb98f6d $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * An interface used to describe the actions required by any type of
 * directory scanner that supports Selecters.
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Bruce Atherton <bruce@callenish.com> (Ant)
 * @package phing.types.selectors
 */
interface SelectorScanner
{

    /**
     * Sets the selectors the scanner should use.
     *
     * @param $selectors the list of selectors
     */
    public function setSelectors($selectors);

    /**
     * Directories which were selected out of a scan.
     *
     * @param selectors list selector objects
     */
    public function getDeselectedDirectories();

    /**
     * Files which were selected out of a scan.
     *
     * @param selectors list selector objects
     */
    public function getDeselectedFiles();

}
<?php

/*
 * $Id: b6db4e2835410eee690766f1a27b83c9e3290466 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/util/StringHelper.php';

/**
 * <p>This is a utility class used by selectors and DirectoryScanner. The
 * functionality more properly belongs just to selectors, but unfortunately
 * DirectoryScanner exposed these as protected methods. Thus we have to
 * support any subclasses of DirectoryScanner that may access these methods.
 * </p>
 * <p>This is a Singleton.</p>
 *
 * @author Hans Lellelid, hans@xmpl.org (Phing)
 * @author Arnout J. Kuiper, ajkuiper@wxs.nl (Ant)
 * @author Magesh Umasankar
 * @author Bruce Atherton, bruce@callenish.com (Ant)
 * @package phing.types.selectors
 */
class SelectorUtils
{

    private static $instance;

    /**
     * Retrieves the instance of the Singleton.
     */
    public static function getInstance()
    {
        if (!isset(self::$instance)) {
            self::$instance = new SelectorUtils();
        }

        return self::$instance;
    }

    /**
     * Tests whether or not a given path matches the start of a given
     * pattern up to the first "**".
     * <p>
     * This is not a general purpose test and should only be used if you
     * can live with false positives. For example, <code>pattern=**\a</code>
     * and <code>str=b</code> will yield <code>true</code>.
     *
     * @param string $pattern
     * @param string $str
     * @param bool $isCaseSensitive
     *
     * @internal param The $pattern pattern to match against. Must not be
     *                <code>null</code>.
     * @internal param The $str path to match, as a String. Must not be
     *                <code>null</code>.
     * @internal param Whether $isCaseSensitive or not matching should be performed
     *                        case sensitively.
     *
     * @return bool whether or not a given path matches the start of a given
     *                 pattern up to the first "**".
     */
    public static function matchPatternStart($pattern, $str, $isCaseSensitive = true)
    {

        // When str starts with a DIRECTORY_SEPARATOR, pattern has to start with a
        // DIRECTORY_SEPARATOR.
        // When pattern starts with a DIRECTORY_SEPARATOR, str has to start with a
        // DIRECTORY_SEPARATOR.
        if (StringHelper::startsWith(DIRECTORY_SEPARATOR, $str) !==
            StringHelper::startsWith(DIRECTORY_SEPARATOR, $pattern)
        ) {
            return false;
        }

        $patDirs = explode(DIRECTORY_SEPARATOR, $pattern);
        $strDirs = explode(DIRECTORY_SEPARATOR, $str);

        $patIdxStart = 0;
        $patIdxEnd = count($patDirs) - 1;
        $strIdxStart = 0;
        $strIdxEnd = count($strDirs) - 1;

        // up to first '**'
        while ($patIdxStart <= $patIdxEnd && $strIdxStart <= $strIdxEnd) {
            $patDir = $patDirs[$patIdxStart];
            if ($patDir == "**") {
                break;
            }
            if (!self::match($patDir, $strDirs[$strIdxStart], $isCaseSensitive)) {
                return false;
            }
            $patIdxStart++;
            $strIdxStart++;
        }

        if ($strIdxStart > $strIdxEnd) {
            // String is exhausted
            return true;
        } elseif ($patIdxStart > $patIdxEnd) {
            // String not exhausted, but pattern is. Failure.
            return false;
        } else {
            // pattern now holds ** while string is not exhausted
            // this will generate false positives but we can live with that.
            return true;
        }
    }

    /**
     * Tests whether or not a given path matches a given pattern.
     *
     * @param string $pattern The pattern to match against. Must not be <code>null</code>.
     * @param string $str The path to match, as a String. Must not be <code>null</code>.
     * @param bool $isCaseSensitive Whether or not matching should be performed case sensitively.
     *
     * @return bool <code>true</code> if the pattern matches against the string,
     */
    public static function matchPath($pattern, $str, $isCaseSensitive = true)
    {
        // explicitly exclude directory itself
        if ($str == '' && $pattern == '**/*') {
            return false;
        }

        $rePattern = preg_quote($pattern, '/');
        $dirSep = preg_quote(DIRECTORY_SEPARATOR, '/');
        $trailingDirSep = '((' . $dirSep . ')?|(' . $dirSep . ').+)';
        $patternReplacements = array(
            $dirSep . '\*\*' . $dirSep => $dirSep . '.*' . $trailingDirSep,
            $dirSep . '\*\*' => $trailingDirSep,
            '\*\*' . $dirSep => '(.*' . $dirSep . ')?',
            '\*\*' => '.*',
            '\*' => '[^' . $dirSep . ']*',
            '\?' => '[^' . $dirSep . ']'
        );
        $rePattern = str_replace(array_keys($patternReplacements), array_values($patternReplacements), $rePattern);
        $rePattern = '/^' . $rePattern . '$/' . ($isCaseSensitive ? '' : 'i');

        return (bool) preg_match($rePattern, $str);
    }

    /**
     * Tests whether or not a string matches against a pattern.
     * The pattern may contain two special characters:<br>
     * '*' means zero or more characters<br>
     * '?' means one and only one character
     *
     * @param string $pattern The pattern to match against.
     *                Must not be <code>null</code>.
     * @param string $str     The string which must be matched against the pattern.
     *                Must not be <code>null</code>.
     * @param bool $isCaseSensitive Whether or not matching should be performed
     *                        case sensitively.
     *
     *
     * @return bool <code>true</code> if the string matches against the pattern,
     *                           or <code>false</code> otherwise.
     */
    public static function match($pattern, $str, $isCaseSensitive = true)
    {
        $rePattern = preg_quote($pattern, '/');
        $rePattern = str_replace(array("\*", "\?"), array('.*', '.'), $rePattern);
        $rePattern = '/^' . $rePattern . '$/' . ($isCaseSensitive ? '' : 'i');

        return (bool) preg_match($rePattern, $str);
    }

    /**
     * Returns dependency information on these two files. If src has been
     * modified later than target, it returns true. If target doesn't exist,
     * it likewise returns true. Otherwise, target is newer than src and
     * is not out of date, thus the method returns false. It also returns
     * false if the src file doesn't even exist, since how could the
     * target then be out of date.
     *
     * @param  PhingFile $src         the original file
     * @param  PhingFile $target      the file being compared against
     * @param  int       $granularity the amount in seconds of slack we will give in
     *                                determining out of dateness
     * @return bool whether   the target is out of date
     */
    public static function isOutOfDate(PhingFile $src, PhingFile $target, $granularity)
    {
        if (!$src->exists()) {
            return false;
        }
        if (!$target->exists()) {
            return true;
        }
        if (($src->lastModified() - $granularity) > $target->lastModified()) {
            return true;
        }

        return false;
    }

    /**
     * @param string $string
     * @return string
     */
    public static function removeWhitespace($string)
    {
        return preg_replace(
            "/(\t|\n|\v|\f|\r| |\xC2\x85|\xc2\xa0|\xe1\xa0\x8e|\xe2\x80[\x80-\x8D]|\xe2\x80\xa8|\xe2\x80\xa9|\xe2\x80\xaF|\xe2\x81\x9f|\xe2\x81\xa0|\xe3\x80\x80|\xef\xbb\xbf)+/",
            '',
            $string
        );
    }
}
<?php

/*
 * $Id: 7c818dba6e4f6075f71fee4beb41767a280706ad $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/selectors/AndSelector.php';

/**
 * This selector just holds one other selector and forwards all
 * requests to it. It exists so that there is a single selector
 * type that can exist outside of any targets, as an element of
 * project. It overrides all of the reference stuff so that it
 * works as expected. Note that this is the only selector you
 * can reference.
 *
 * @author    Hans Lellelid <hans@xmpl.org> (Phing)
 * @author    Bruce Atherton <bruce@callenish.com> (Ant)
 * @version   $Id: 7c818dba6e4f6075f71fee4beb41767a280706ad $
 * @package   phing.types.selectors
 */
class SelectSelector extends AndSelector
{

    /**
     * @return string
     */
    public function toString()
    {
        $buf = "";
        if ($this->hasSelectors()) {
            $buf .= "{select: ";
            $buf .= parent::toString();
            $buf .= "}";
        }

        return $buf;
    }

    /**
     * Performs the check for circular references and returns the
     * referenced Selector.
     */
    private function getRef()
    {
        $o = $this->getCheckedRef(get_class($this), "SelectSelector");

        return $o;
    }

    /**
     * Indicates whether there are any selectors here.
     */
    public function hasSelectors()
    {
        if ($this->isReference()) {
            return $this->getRef()->hasSelectors();
        }

        return parent::hasSelectors();
    }

    /**
     * Gives the count of the number of selectors in this container
     */
    public function selectorCount()
    {
        if ($this->isReference()) {
            return $this->getRef()->selectorCount();
        }

        return parent::selectorCount();
    }

    /**
     * Returns the set of selectors as an array.
     * @param Project $p
     * @return \an|array
     */
    public function getSelectors(Project $p)
    {
        if ($this->isReference()) {
            return $this->getRef()->getSelectors($p);
        }

        return parent::getSelectors($p);
    }

    /**
     * Returns an enumerator for accessing the set of selectors.
     */
    public function selectorElements()
    {
        if ($this->isReference()) {
            return $this->getRef()->selectorElements();
        }

        return parent::selectorElements();
    }

    /**
     * Add a new selector into this container.
     *
     * @param FileSelector|the $selector
     * @throws BuildException
     * @internal param the $selector new selector to add
     * @return the selector that was added
     */
    public function appendSelector(FileSelector $selector)
    {
        if ($this->isReference()) {
            throw $this->noChildrenAllowed();
        }
        parent::appendSelector($selector);
    }

    /**
     * Makes sure that there is only one entry, sets an error message if
     * not.
     */
    public function verifySettings()
    {
        if ($this->selectorCount() != 1) {
            $this->setError(
                "One and only one selector is allowed within the "
                . "<selector> tag"
            );
        }
    }

}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Selector that filters files based on their size.
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Bruce Atherton <bruce@callenish.com> (Ant)
 *
 * @package phing.types.selectors
 */
class SizeSelector extends BaseExtendSelector
{
    /** @var int $size */
    private $size = -1;

    /** @var int $multiplier */
    private $multiplier = 1;

    /** @var int $sizelimit */
    private $sizelimit = -1;

    /** @var int $cmp */
    private $cmp = 2;

    const SIZE_KEY = "value";
    const UNITS_KEY = "units";
    const WHEN_KEY = "when";

    /** @var array $sizeComparisons */
    private static $sizeComparisons = array("less", "more", "equal");

    /** @var array $byteUnits */
    private static $byteUnits = array(
        "K",
        "k",
        "kilo",
        "KILO",
        "Ki",
        "KI",
        "ki",
        "kibi",
        "KIBI",
        "M",
        "m",
        "mega",
        "MEGA",
        "Mi",
        "MI",
        "mi",
        "mebi",
        "MEBI",
        "G",
        "g",
        "giga",
        "GIGA",
        "Gi",
        "GI",
        "gi",
        "gibi",
        "GIBI",
        "T",
        "t",
        "tera",
        "TERA",
        /* You wish! */
        "Ti",
        "TI",
        "ti",
        "tebi",
        "TEBI"
    );

    /**
     * @return string
     */
    public function toString()
    {
        $buf = "{sizeselector value: ";
        $buf .= $this->sizelimit;
        $buf .= "compare: ";
        if ($this->cmp === 0) {
            $buf .= "less";
        } elseif ($this->cmp === 1) {
            $buf .= "more";
        } else {
            $buf .= "equal";
        }
        $buf .= "}";

        return $buf;
    }

    /**
     * A size selector needs to know what size to base its selecting on.
     * This will be further modified by the multiplier to get an
     * actual size limit.
     *
     * @param int $size the size to select against expressed in units
     *
     * @return void
     */
    public function setValue($size)
    {
        $this->size = $size;
        if (($this->multiplier !== 0) && ($this->size > -1)) {
            $this->sizelimit = $size * $this->multiplier;
        }
    }

    /**
     * Sets the units to use for the comparison. This is a little
     * complicated because common usage has created standards that
     * play havoc with capitalization rules. Thus, some people will
     * use "K" for indicating 1000's, when the SI standard calls for
     * "k". Others have tried to introduce "K" as a multiple of 1024,
     * but that falls down when you reach "M", since "m" is already
     * defined as 0.001.
     * <p>
     * To get around this complexity, a number of standards bodies
     * have proposed the 2^10 standard, and at least one has adopted
     * it. But we are still left with a populace that isn't clear on
     * how capitalization should work.
     * <p>
     * We therefore ignore capitalization as much as possible.
     * Completely mixed case is not possible, but all upper and lower
     * forms are accepted for all long and short forms. Since we have
     * no need to work with the 0.001 case, this practice works here.
     * <p>
     * This function translates all the long and short forms that a
     * unit prefix can occur in and translates them into a single
     * multiplier.
     *
     * @param array $units The units to compare the size to.
     *
     * @return void
     */
    public function setUnits($units)
    {
        $i = array_search($units, self::$byteUnits, true);
        if ($i === false) {
            $i = -1;
        } // make it java-like

        $this->multiplier = 0;
        if (($i > -1) && ($i < 4)) {
            $this->multiplier = 1000;
        } elseif (($i > 3) && ($i < 9)) {
            $this->multiplier = 1024;
        } elseif (($i > 8) && ($i < 13)) {
            $this->multiplier = 1000000;
        } elseif (($i > 12) && ($i < 18)) {
            $this->multiplier = 1048576;
        } elseif (($i > 17) && ($i < 22)) {
            $this->multiplier = 1000000000;
        } elseif (($i > 21) && ($i < 27)) {
            $this->multiplier = 1073741824;
        } elseif (($i > 26) && ($i < 31)) {
            $this->multiplier = 1000000000000;
        } elseif (($i > 30) && ($i < 36)) {
            $this->multiplier = 1099511627776;
        }
        if (($this->multiplier > 0) && ($this->size > -1)) {
            $this->sizelimit = $this->size * $this->multiplier;
        }
    }

    /**
     * This specifies when the file should be selected, whether it be
     * when the file matches a particular size, when it is smaller,
     * or whether it is larger.
     *
     * @param array $cmp The comparison to perform, an EnumeratedAttribute
     *
     * @return void
     */
    public function setWhen($cmp)
    {
        $c = array_search($cmp, self::$sizeComparisons, true);
        if ($c !== false) {
            $this->cmp = $c;
        }
    }

    /**
     * When using this as a custom selector, this method will be called.
     * It translates each parameter into the appropriate setXXX() call.
     *
     * {@inheritdoc}
     *
     * @param array $parameters the complete set of parameters for this selector
     *
     * @return void
     *
     * @throws BuildException
     */
    public function setParameters($parameters)
    {
        parent::setParameters($parameters);
        if ($parameters !== null) {
            for ($i = 0, $size = count($parameters); $i < $size; $i++) {
                $paramname = $parameters[$i]->getName();
                switch (strtolower($paramname)) {
                    case self::SIZE_KEY:
                        try {
                            $this->setValue($parameters[$i]->getValue());
                        } catch (Exception $nfe) {
                            $this->setError(
                                "Invalid size setting "
                                . $parameters[$i]->getValue()
                            );
                        }
                        break;
                    case self::UNITS_KEY:
                        $this->setUnits($parameters[$i]->getValue());
                        break;
                    case self::WHEN_KEY:
                        $this->setWhen($parameters[$i]->getValue());
                        break;
                    default:
                        $this->setError("Invalid parameter " . $paramname);
                }
            }
        }
    }

    /**
     * <p>Checks to make sure all settings are kosher. In this case, it
     * means that the size attribute has been set (to a positive value),
     * that the multiplier has a valid setting, and that the size limit
     * is valid. Since the latter is a calculated value, this can only
     * fail due to a programming error.
     * </p>
     * <p>If a problem is detected, the setError() method is called.
     * </p>
     *
     * {@inheritdoc}
     *
     * @return void
     */
    public function verifySettings()
    {
        if ($this->size < 0) {
            $this->setError("The value attribute is required, and must be positive");
        } elseif ($this->multiplier < 1) {
            $this->setError("Invalid Units supplied, must be K,Ki,M,Mi,G,Gi,T,or Ti");
        } elseif ($this->sizelimit < 0) {
            $this->setError("Internal error: Code is not setting sizelimit correctly");
        }
    }

    /**
     * The heart of the matter. This is where the selector gets to decide
     * on the inclusion of a file in a particular fileset.
     *
     * {@inheritdoc}
     *
     * @param PhingFile $basedir A PhingFile object for the base directory
     * @param string $filename The name of the file to check
     * @param PhingFile $file A PhingFile object for this filename
     *
     * @return bool whether the file should be selected or not
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
    {
        $this->validate();

        // Directory size never selected for
        if ($file->isDirectory()) {
            return true;
        }
        if ($this->cmp === 0) {
            return ($file->length() < $this->sizelimit);
        } elseif ($this->cmp === 1) {
            return ($file->length() > $this->sizelimit);
        } else {
            return ($file->length() === $this->sizelimit);
        }
    }
}
<?php

/*
 * $Id: a2775fcd20fded1d1bf890e4d77286dd0325ef22 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/selectors/BaseExtendSelector.php';

/**
 * Selector that selects a certain kind of file: directory or regular file.
 *
 * @author    Hans Lellelid <hans@xmpl.org> (Phing)
 * @author    Jeff Turner <jefft@apache.org> (Ant)
 * @version   $Id: a2775fcd20fded1d1bf890e4d77286dd0325ef22 $
 * @package   phing.types.selectors
 */
class TypeSelector extends BaseExtendSelector
{

    private $type;

    /** Key to used for parameterized custom selector */
    const TYPE_KEY = "type";

    /** Valid types */
    private static $types = array('file', 'dir', 'link');

    /**
     * @return string A string describing this object
     */
    public function toString()
    {
        $buf = "{typeselector type: " . $this->type . "}";

        return $buf;
    }

    /**
     * Set the type of file to require.
     * @param string $type The type of file - 'file' or 'dir'
     */
    public function setType($type)
    {
        $this->type = $type;
    }

    /**
     * When using this as a custom selector, this method will be called.
     * It translates each parameter into the appropriate setXXX() call.
     *
     * @param array $parameters the complete set of parameters for this selector
     * @return mixed|void
     */
    public function setParameters($parameters)
    {
        parent::setParameters($parameters);
        if ($parameters !== null) {
            for ($i = 0, $size = count($parameters); $i < $size; $i++) {
                $paramname = $parameters[$i]->getName();
                if (self::TYPE_KEY == strtolower($paramname)) {
                    $this->setType($parameters[$i]->getValue());
                } else {
                    $this->setError("Invalid parameter " . $paramname);
                }
            }
        }
    }

    /**
     * Checks to make sure all settings are kosher. In this case, it
     * means that the pattern attribute has been set.
     *
     */
    public function verifySettings()
    {
        if ($this->type === null) {
            $this->setError("The type attribute is required");
        } elseif (!in_array($this->type, self::$types, true)) {
            $this->setError("Invalid type specified; must be one of (" . implode(self::$types) . ")");
        }
    }

    /**
     * The heart of the matter. This is where the selector gets to decide
     * on the inclusion of a file in a particular fileset.
     *
     * @param  PhingFile $basedir  the base directory the scan is being done from
     * @param  string    $filename is the name of the file to check
     * @param  PhingFile $file     is a PhingFile object the selector can use
     * @return boolean   Whether the file should be selected or not
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
    {
        // throw BuildException on error
        $this->validate();

        if ($file->isLink()) {
            if ($this->type == 'link') {
                return true;
            }

            $this->log(
                $file->getAbsolutePath() . " is a link, proceeding with " . $file->getCanonicalPath() . " instead.",
                Project::MSG_DEBUG
            );
            $file = new PhingFile($file->getCanonicalPath());
        }

        if ($file->isDirectory()) {
            return $this->type === 'dir';
        } else {
            return $this->type === 'file';
        }
    }

}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/types/selectors/FileSelector.php';

/**
 * A selector that selects writable files.
 *
 * @author  Siad Ardroumli <siad.ardroumli@gmail.com>
 * @package phing.types.selectors
 */
class WritableSelector implements FileSelector
{
    /**
     * {@inheritdoc}
     *
     * @param PhingFile $basedir
     * @param string $filename
     * @param PhingFile $file
     *
     * @return bool
     */
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
    {
        return $file !== null && $file->canWrite();
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

// include_once 'phing/system/io/Reader.php'; // really this is unrelated to Reader
include_once 'phing/system/io/IOException.php';
include_once 'phing/filters/ReplaceTokens.php'; // For class Token

/**
 * Abstract class for TokenReaders.
 *
 * @author    Manuel Holtgewe
 *
 * @package   phing.filters.util
 */
abstract class TokenReader
{
    /**
     * Reference to the Project the TokenReader is used in.
     *
     * @var Project
     */
    protected $project;

    /**
     * Constructor.
     *
     * @param Project $project Reference to the project the TokenReader is used in.
     */
    public function __construct(Project $project)
    {
        $this->project = $project;
    }

    /**
     * Utility function for logging.
     *
     * @param $level
     * @param $msg
     *
     * @return void
     */
    public function log($level, $msg)
    {
        $this->project->log($level, $msg);
    }

    /**
     * Reads the next token from the Reader.
     *
     * @throws IOException - On error
     *
     * @return string
     */
    abstract public function readToken();

}
<?php
/*
 *  $Id: 8364cd2c776b9cc8e544da4eb56cf96ef5b4e7df $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
*/

require_once 'phing/types/DataType.php';
include_once 'phing/util/StringHelper.php';

/**
 * A parameter is composed of a name, type and value.
 *
 * Example of usage:
 *
 * <replacetokens>
 *   <tokensource classname="phing.filters.util.IniFileTokenReader">
 *     <!-- all params for the TokenReader here -->
 *     <param name="file" value="tokens.ini" />
 *   </tokensource>
 * </replacetokens>
 *
 * or:
 *
 * <filterreader classname="phing.filters.ReplaceTokens">
 *   <param type="tokensource>
 *     <param name="classname" value="phing.filters.util.IniFileTokenReader" />
 *     <param name="file" value="tokens.ini" />
 *   </param>
 * </filterreader>
 *
 * @author    <a href="mailto:yl@seasonfive.com">Yannick Lecaillez</a>
 * @package   phing.types
 */
class TokenSource extends DataType
{

    /**
     * String to hold the path to the TokenReader
     * @var     string
     */
    protected $classname = null;

    /**
     * Array holding parameters for the wrapped TokenReader.
     * @var array
     */
    protected $parameters = array();

    /**
     * Reference to the TokenReader used by this TokenSource
     * @var TokenReader
     */
    protected $reader;

    /**
     * Array with key/value pairs of tokens
     */
    protected $tokens = array();

    /**
     * This method is called to load the sources from the reader
     * into the buffer of the source.
     */
    public function load()
    {
        // Create new Reader
        if ($this->classname === null) {
            throw new BuildException("No Classname given to TokenSource.");
        }

        $classname = Phing::import($this->classname);
        $this->reader = new $classname($this->project);

        // Configure Reader
        $this->configureTokenReader($this->reader);

        // Load Tokens
        try {
            while ($token = $this->reader->readToken()) {
                $this->tokens[] = $token;
            }
        } catch (BuildException $e) {
            $this->log("Error reading TokenSource: " . $e->getMessage(), Project::MSG_WARN);
        } catch (IOException $e) {
            $this->log("Error reading TokenSource: " . $e->getMessage(), Project::MSG_WARN);
        }
    }

    /**
     * This function uses the wrapper to read the tokens and then
     * returns them.
     *
     */
    public function getTokens()
    {
        if (count($this->tokens) == 0) {
            $this->Load();
        }

        return $this->tokens;
    }

    /**
     * Configures a TokenReader with the parameters passed to the
     * TokenSource.
     * @param TokenReader $reader
     */
    private function configureTokenReader(TokenReader $reader)
    {
        $count = count($this->parameters);
        for ($i = 0; $i < $count; $i++) {
            $method_name = "Set" . $this->parameters[$i]->getName();
            $value = $this->parameters[$i]->getValue();
            $reader->$method_name($value);
        }
    }

    /**
     * Set the classname (dot-path) to use for handling token replacement.
     * @param string $c
     */
    public function setClassname($c)
    {
        $this->classname = $c;
    }

    /**
     * Returns the qualified classname (dot-path) to use for handling token replacement.
     * @return string
     */
    public function getClassname()
    {
        return $this->classname;
    }

    /**
     * Create nested <param> tag.
     * Uses standard name/value Parameter class.
     * @return Parameter
     */
    public function createParam()
    {
        $num = array_push($this->parameters, new Parameter());

        return $this->parameters[$num - 1];
    }
}
<?php
/*
 *  $Id: 65dd645aab6f4357dacee3260c63aa716e3a8780 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/Task.php';

/**
 * Wrapper class that holds all information necessary to create a task
 * that did not exist when Phing started.
 *
 * <em> This has something to do with phing encountering an task XML element
 * it is not aware of at start time. This is a situation where special steps
 * need to be taken so that the element is then known.</em>
 *
 * @author    Andreas Aderhold <andi@binarycloud.com>
 * @author    Hans Lellelid <hans@xmpl.org>
 * @version   $Id: 65dd645aab6f4357dacee3260c63aa716e3a8780 $
 * @package   phing
 */
class UnknownElement extends Task
{

    private $elementName;
    private $realThing;
    private $children = array();

    /**
     * Constructs a UnknownElement object
     *
     * @param    string  The XML element name that is unknown
     */
    public function __construct($elementName)
    {
        $this->elementName = (string) $elementName;
    }

    /**
     * Return the XML element name that this <code>UnnownElement</code>
     * handles.
     *
     * @return string The XML element name that is unknown
     */
    public function getTag()
    {
        return (string) $this->elementName;
    }

    /**
     * Tries to configure the unknown element
     *
     * @throws BuildException if the element can not be configured
     */
    public function maybeConfigure()
    {

        $this->realThing = $this->makeObject($this, $this->wrapper);
        $this->wrapper->setProxy($this->realThing);
        if ($this->realThing instanceof Task) {
            $this->realThing->setRuntimeConfigurableWrapper($this->wrapper);
            $this->realThing->maybeConfigure();
        } else {
            $this->wrapper->maybeConfigure($this->getProject());
        }
        $this->handleChildren($this->realThing, $this->wrapper);

    }

    /**
     * Called when the real task has been configured for the first time.
     *
     * @throws BuildException if the task can not be created
     */
    public function main()
    {

        if ($this->realThing === null) {
            // plain impossible to get here, maybeConfigure should
            // have thrown an exception.
            throw new BuildException("Should not be executing UnknownElement::main() -- task/type: {$this->elementName}");
        }

        if ($this->realThing instanceof Task) {
            $this->realThing->main();
        }

    }

    /**
     * Add a child element to the unknown element
     *
     * @param UnknownElement $child
     * @internal param The $object object representing the child element
     */
    public function addChild(UnknownElement $child)
    {
        $this->children[] = $child;
    }

    /**
     *  Handle child elemets of the unknown element, if any.
     *
     * @param object $parent        The parent object the unknown element belongs to
     * @param object $parentWrapper The parent wrapper object
     */
    public function handleChildren($parent, $parentWrapper)
    {

        if ($parent instanceof TaskAdapter) {
            $parent = $parent->getProxy();
        }

        $parentClass = get_class($parent);
        $ih = IntrospectionHelper::getHelper($parentClass);

        for ($i = 0, $childrenCount = count($this->children); $i < $childrenCount; $i++) {

            $childWrapper = $parentWrapper->getChild($i);
            $child = $this->children[$i];

            $realChild = null;
            if ($parent instanceof TaskContainer) {
                $parent->addTask($child);
                continue;
            }

            $project = $this->project === null ? $parent->project : $this->project;
            $realChild = $ih->createElement($project, $parent, $child->getTag());

            $childWrapper->setProxy($realChild);
            if ($realChild instanceof Task) {
                $realChild->setRuntimeConfigurableWrapper($childWrapper);
            }

            $childWrapper->maybeConfigure($this->project);
            $child->handleChildren($realChild, $childWrapper);
        }
    }

    /**
     * @param IntrospectionHelper $ih
     * @param $parent
     * @param UnknownElement $child
     * @param RuntimeConfigurable $childWrapper
     * @return bool
     */
    public function handleChild(
        IntrospectionHelper $ih,
        $parent,
        UnknownElement $child,
        RuntimeConfigurable $childWrapper
    ) {
        $childWrapper->setProxy($realChild);
        if ($realChild instanceof Task) {
            $realChild->setRuntimeConfigurableWrapper($childWrapper);
        }

        $childWrapper->maybeConfigure($this->project);
        $child->handleChildren($realChild, $childWrapper);

        return true;
    }

    /**
     * Creates a named task or data type. If the real object is a task,
     * it is configured up to the init() stage.
     *
     * @param  UnknownElement $ue The unknown element to create the real object for.
     *                                 Must not be <code>null</code>.
     * @param  RuntimeConfigurable $w Ignored in this implementation.
     * @throws BuildException
     * @return object              The Task or DataType represented by the given unknown element.
     */
    protected function makeObject(UnknownElement $ue, RuntimeConfigurable $w)
    {
        $o = $this->makeTask($ue, $w, true);
        if ($o === null) {
            $o = $this->project->createDataType($ue->getTag());
        }
        if ($o === null) {
            throw new BuildException("Could not create task/type: '" . $ue->getTag(
                ) . "'. Make sure that this class has been declared using taskdef / typedef.");
        }

        return $o;
    }

    /**
     *  Create a named task and configure it up to the init() stage.
     *
     * @param  UnknownElement $ue The unknwon element to create a task from
     * @param  RuntimeConfigurable $w The wrapper object
     * @param  boolean $onTopLevel Whether to treat this task as if it is top-level.
     * @throws BuildException
     * @return Task                The freshly created task
     */
    protected function makeTask(UnknownElement $ue, RuntimeConfigurable $w, $onTopLevel = false)
    {

        $task = $this->project->createTask($ue->getTag());

        if ($task === null) {
            if (!$onTopLevel) {
                throw new BuildException("Could not create task of type: '" . $this->elementName . "'. Make sure that this class has been declared using taskdef.");
            }

            return null;
        }

        // used to set the location within the xmlfile so that exceptions can
        // give detailed messages

        $task->setLocation($this->getLocation());
        $attrs = $w->getAttributes();
        if (isset($attrs['id'])) {
            $this->project->addReference($attrs['id'], $task);
        }

        if ($this->target !== null) {
            $task->setOwningTarget($this->target);
        }

        $task->init();

        return $task;
    }

    /**
     *  Get the name of the task to use in logging messages.
     *
     * @return string The task's name
     */
    public function getTaskName()
    {
        return $this->realThing === null || !$this->realThing instanceof Task
            ? parent::getTaskName()
            : $this->realThing->getTaskName();
    }
}
<?php

/*
 *  $Id: 33628948aa5dc0ffef8f645df415c37b4a6268f2 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/PhingFile.php';
require_once 'phing/system/io/FileWriter.php';

/**
 * An abstract representation of file and directory pathnames.
 *
 * @package phing.util
 * @author  Michiel Rook <mrook@php.net>
 * @version $Id: 33628948aa5dc0ffef8f645df415c37b4a6268f2 $
 */
class DataStore
{
    private $data = array();
    private $file = null;

    /**
     * Constructs a new data store
     *
     * @param PhingFile $file object pointing to the data store on disk
     */
    public function __construct(PhingFile $file)
    {
        $this->file = $file;

        if ($this->file->exists()) {
            $this->read();
        }
    }

    /**
     * Destructor
     */
    public function __destruct()
    {
        $this->commit();
    }

    /**
     * Retrieves a value from the data store
     *
     * @param string $key the key
     *
     * @return mixed the value
     */
    public function get($key)
    {
        if (!isset($this->data[$key])) {
            return null;
        } else {
            return $this->data[$key];
        }
    }

    /**
     * Adds a value to the data store
     *
     * @param string  $key        the key
     * @param mixed   $value      the value
     * @param boolean $autocommit whether to auto-commit (write)
     *                            the data store to disk
     *
     * @return none
     */
    public function put($key, $value, $autocommit = false)
    {
        $this->data[$key] = $value;

        if ($autocommit) {
            $this->commit();
        }
    }

    /**
     * Remove a value from the data store
     *
     * @param string  $key        the key
     * @param boolean $autocommit whether to auto-commit (write)
     *                            the data store to disk
     */
    public function remove($key, $autocommit = false)
    {
        unset($this->data[$key]);

        if ($autocommit) {
            $this->commit();
        }
    }

    /**
     * Commits data store to disk
     *
     * @return none
     */
    public function commit()
    {
        $this->write();
    }

    /**
     * Internal function to read data store from file
     *
     * @throws BuildException
     * @return none
     */
    private function read()
    {
        if (!$this->file->canRead()) {
            throw new BuildException("Can't read data store from '" .
                $this->file->getPath() . "'");
        } else {
            $serializedData = $this->file->contents();

            $this->data = unserialize($serializedData);
        }
    }

    /**
     * Internal function to write data store to file
     *
     * @throws BuildException
     * @return none
     */
    private function write()
    {
        if (!$this->file->canWrite()) {
            throw new BuildException("Can't write data store to '" .
                $this->file->getPath() . "'");
        } else {
            $serializedData = serialize($this->data);

            $writer = new FileWriter($this->file);
            $writer->write($serializedData);
            $writer->close();
        }
    }
}

;
<?php
/*
 *  $Id: 90512f0542e101f1d60b89c2c9d77da1dd90137f $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/types/selectors/SelectorScanner.php';
include_once 'phing/util/StringHelper.php';
include_once 'phing/types/selectors/SelectorUtils.php';

/**
 * Class for scanning a directory for files/directories that match a certain
 * criteria.
 *
 * These criteria consist of a set of include and exclude patterns. With these
 * patterns, you can select which files you want to have included, and which
 * files you want to have excluded.
 *
 * The idea is simple. A given directory is recursively scanned for all files
 * and directories. Each file/directory is matched against a set of include
 * and exclude patterns. Only files/directories that match at least one
 * pattern of the include pattern list, and don't match a pattern of the
 * exclude pattern list will be placed in the list of files/directories found.
 *
 * When no list of include patterns is supplied, "**" will be used, which
 * means that everything will be matched. When no list of exclude patterns is
 * supplied, an empty list is used, such that nothing will be excluded.
 *
 * The pattern matching is done as follows:
 * The name to be matched is split up in path segments. A path segment is the
 * name of a directory or file, which is bounded by DIRECTORY_SEPARATOR
 * ('/' under UNIX, '\' under Windows).
 * E.g. "abc/def/ghi/xyz.php" is split up in the segments "abc", "def", "ghi"
 * and "xyz.php".
 * The same is done for the pattern against which should be matched.
 *
 * Then the segments of the name and the pattern will be matched against each
 * other. When '**' is used for a path segment in the pattern, then it matches
 * zero or more path segments of the name.
 *
 * There are special case regarding the use of DIRECTORY_SEPARATOR at
 * the beginning of the pattern and the string to match:
 * When a pattern starts with a DIRECTORY_SEPARATOR, the string
 * to match must also start with a DIRECTORY_SEPARATOR.
 * When a pattern does not start with a DIRECTORY_SEPARATOR, the
 * string to match may not start with a DIRECTORY_SEPARATOR.
 * When one of these rules is not obeyed, the string will not
 * match.
 *
 * When a name path segment is matched against a pattern path segment, the
 * following special characters can be used:
 *   '*' matches zero or more characters,
 *   '?' matches one character.
 *
 * Examples:
 *
 * "**\*.php" matches all .php files/dirs in a directory tree.
 *
 * "test\a??.php" matches all files/dirs which start with an 'a', then two
 * more characters and then ".php", in a directory called test.
 *
 * "**" matches everything in a directory tree.
 *
 * "**\test\**\XYZ*" matches all files/dirs that start with "XYZ" and where
 * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123").
 *
 * Case sensitivity may be turned off if necessary.  By default, it is
 * turned on.
 *
 * Example of usage:
 *   $ds = new DirectroyScanner();
 *   $includes = array("**\*.php");
 *   $excludes = array("modules\*\**");
 *   $ds->SetIncludes($includes);
 *   $ds->SetExcludes($excludes);
 *   $ds->SetBasedir("test");
 *   $ds->SetCaseSensitive(true);
 *   $ds->Scan();
 *
 *   print("FILES:");
 *   $files = ds->GetIncludedFiles();
 *   for ($i = 0; $i < count($files);$i++) {
 *     println("$files[$i]\n");
 *   }
 *
 * This will scan a directory called test for .php files, but excludes all
 * .php files in all directories under a directory called "modules"
 *
 * This class is complete preg/ereg free port of the Java class
 * org.apache.tools.ant.DirectoryScanner. Even functions that use preg/ereg
 * internally (like split()) are not used. Only the _fast_ string functions
 * and comparison operators (=== !=== etc) are used for matching and tokenizing.
 *
 * @author   Arnout J. Kuiper, ajkuiper@wxs.nl
 * @author   Magesh Umasankar, umagesh@rediffmail.com
 * @author   Andreas Aderhold, andi@binarycloud.com
 *
 * @version   $Id: 90512f0542e101f1d60b89c2c9d77da1dd90137f $
 * @package   phing.util
 */
class DirectoryScanner implements SelectorScanner
{

    /** default set of excludes */
    protected $DEFAULTEXCLUDES = array(
        "**/*~",
        "**/#*#",
        "**/.#*",
        "**/%*%",
        "**/CVS",
        "**/CVS/**",
        "**/.cvsignore",
        "**/SCCS",
        "**/SCCS/**",
        "**/vssver.scc",
        "**/.svn",
        "**/.svn/**",
        "**/._*",
        "**/.DS_Store",
        "**/.darcs",
        "**/.darcs/**",
        "**/.git",
        "**/.git/**",
        "**/.gitattributes",
        "**/.gitignore",
        "**/.gitmodules",
        "**/.hg",
        "**/.hg/**",
        "**/.hgignore",
        "**/.hgsub",
        "**/.hgsubstate",
        "**/.hgtags",
        "**/.bzr",
        "**/.bzr/**",
        "**/.bzrignore",
    );

    /** The base directory which should be scanned. */
    protected $basedir;

    /** The patterns for the files that should be included. */
    protected $includes = null;

    /** The patterns for the files that should be excluded. */
    protected $excludes = null;

    /** Whether to expand/dereference symbolic links, default is false */
    protected $expandSymbolicLinks = false;

    /**
     * The files that where found and matched at least one includes, and matched
     * no excludes.
     */
    protected $filesIncluded;

    /** The files that where found and did not match any includes. Trie */
    protected $filesNotIncluded;

    /**
     * The files that where found and matched at least one includes, and also
     * matched at least one excludes. Trie object.
     */
    protected $filesExcluded;

    /**
     * The directories that where found and matched at least one includes, and
     * matched no excludes.
     */
    protected $dirsIncluded;

    /** The directories that where found and did not match any includes. */
    protected $dirsNotIncluded;

    /**
     * The files that where found and matched at least one includes, and also
     * matched at least one excludes.
     */
    protected $dirsExcluded;

    /** Have the vars holding our results been built by a slow scan? */
    protected $haveSlowResults = false;

    /** Should the file system be treated as a case sensitive one? */
    protected $isCaseSensitive = true;

    /** Selectors */
    protected $selectors = null;

    protected $filesDeselected;
    protected $dirsDeselected;

    /** if there are no deselected files */
    protected $everythingIncluded = true;

    /**
     * Does the path match the start of this pattern up to the first "**".
     * This is a static mehtod and should always be called static
     *
     * This is not a general purpose test and should only be used if you
     * can live with false positives.
     *
     * pattern=**\a and str=b will yield true.
     *
     * @param string $pattern
     * @param string $str
     * @param bool $isCaseSensitive
     * @internal param the $pattern pattern to match against
     * @internal param the $str string (path) to match
     * @internal param must $isCaseSensitive matches be case sensitive?
     * @return boolean true if matches, otherwise false
     */
    public function matchPatternStart($pattern, $str, $isCaseSensitive = true)
    {
        return SelectorUtils::matchPatternStart($pattern, $str, $isCaseSensitive);
    }

    /**
     * Matches a path against a pattern.
     *
     * @param string $pattern        the (non-null) pattern to match against
     * @param string $str            the (non-null) string (path) to match
     * @param bool $isCaseSensitive  must a case sensitive match be done?
     *
     * @return bool true when the pattern matches against the string.
     *              false otherwise.
     */
    public function matchPath($pattern, $str, $isCaseSensitive = true)
    {
        return SelectorUtils::matchPath($pattern, $str, $isCaseSensitive);
    }

    /**
     * Matches a string against a pattern. The pattern contains two special
     * characters:
     * '*' which means zero or more characters,
     * '?' which means one and only one character.
     *
     * @param string $pattern pattern to match against
     * @param string $str string that must be matched against the
     *                    pattern
     * @param bool $isCaseSensitive
     *
     * @return boolean true when the string matches against the pattern,
     *                 false otherwise.
     */
    public function match($pattern, $str, $isCaseSensitive = true)
    {
        return SelectorUtils::match($pattern, $str, $isCaseSensitive);
    }

    /**
     * Sets the basedir for scanning. This is the directory that is scanned
     * recursively. All '/' and '\' characters are replaced by
     * DIRECTORY_SEPARATOR
     *
     * @param basedir the (non-null) basedir for scanning
     */
    public function setBasedir($_basedir)
    {
        $_basedir = str_replace('\\', DIRECTORY_SEPARATOR, $_basedir);
        $_basedir = str_replace('/', DIRECTORY_SEPARATOR, $_basedir);
        $this->basedir = $_basedir;
    }

    /**
     * Gets the basedir that is used for scanning. This is the directory that
     * is scanned recursively.
     *
     * @return string the basedir that is used for scanning
     */
    public function getBasedir()
    {
        return $this->basedir;
    }

    /**
     * Sets the case sensitivity of the file system
     *
     * @param bool $_isCaseSensitive specifies if the filesystem is case sensitive
     */
    public function setCaseSensitive($_isCaseSensitive)
    {
        $this->isCaseSensitive = ($_isCaseSensitive) ? true : false;
    }

    /**
     * Sets the set of include patterns to use. All '/' and '\' characters are
     * replaced by DIRECTORY_SEPARATOR. So the separator used need
     * not match DIRECTORY_SEPARATOR.
     *
     * When a pattern ends with a '/' or '\', "**" is appended.
     *
     * @param array $_includes
     * @internal param list $includes of include patterns
     */
    public function setIncludes($_includes = array())
    {
        if (empty($_includes) || is_null($_includes)) {
            $this->includes = null;
        } else {
            for ($i = 0; $i < count($_includes); $i++) {
                $pattern = null;
                $pattern = str_replace('\\', DIRECTORY_SEPARATOR, $_includes[$i]);
                $pattern = str_replace('/', DIRECTORY_SEPARATOR, $pattern);
                if (StringHelper::endsWith(DIRECTORY_SEPARATOR, $pattern)) {
                    $pattern .= "**";
                }
                $this->includes[] = $pattern;
            }
        }
    }

    /**
     * Sets the set of exclude patterns to use. All '/' and '\' characters are
     * replaced by <code>File.separatorChar</code>. So the separator used need
     * not match <code>File.separatorChar</code>.
     *
     * When a pattern ends with a '/' or '\', "**" is appended.
     *
     * @param array $_excludes
     * @internal param list $excludes of exclude patterns
     */

    public function setExcludes($_excludes = array())
    {
        if (empty($_excludes) || is_null($_excludes)) {
            $this->excludes = null;
        } else {
            for ($i = 0; $i < count($_excludes); $i++) {
                $pattern = null;
                $pattern = str_replace('\\', DIRECTORY_SEPARATOR, $_excludes[$i]);
                $pattern = str_replace('/', DIRECTORY_SEPARATOR, $pattern);
                if (StringHelper::endsWith(DIRECTORY_SEPARATOR, $pattern)) {
                    $pattern .= "**";
                }
                $this->excludes[] = $pattern;
            }
        }
    }

    /**
     * Sets whether to expand/dereference symbolic links
     *
     * @param boolean $expandSymbolicLinks
     */
    public function setExpandSymbolicLinks($expandSymbolicLinks)
    {
        $this->expandSymbolicLinks = $expandSymbolicLinks;
    }

    /**
     * Scans the base directory for files that match at least one include
     * pattern, and don't match any exclude patterns.
     *
     */
    public function scan()
    {

        if ((empty($this->basedir)) || (!@is_dir($this->basedir))) {
            return false;
        }

        if ($this->includes === null) {
            // No includes supplied, so set it to 'matches all'
            $this->includes = array("**");
        }
        if (is_null($this->excludes)) {
            $this->excludes = array();
        }

        $this->filesIncluded = array();
        $this->filesNotIncluded = array();
        $this->filesExcluded = array();
        $this->dirsIncluded = array();
        $this->dirsNotIncluded = array();
        $this->dirsExcluded = array();
        $this->dirsDeselected = array();
        $this->filesDeselected = array();

        if ($this->isIncluded("")) {
            if (!$this->isExcluded("")) {
                if ($this->isSelected("", $this->basedir)) {
                    $this->dirsIncluded[] = "";
                } else {
                    $this->dirsDeselected[] = "";
                }
            } else {
                $this->dirsExcluded[] = "";
            }
        } else {
            $this->dirsNotIncluded[] = "";
        }

        $this->scandir($this->basedir, "", true);

        return true;
    }

    /**
     * Toplevel invocation for the scan.
     *
     * Returns immediately if a slow scan has already been requested.
     */
    protected function slowScan()
    {

        if ($this->haveSlowResults) {
            return;
        }

        // copy trie object add CopyInto() method
        $excl = $this->dirsExcluded;
        $notIncl = $this->dirsNotIncluded;

        for ($i = 0, $_i = count($excl); $i < $_i; $i++) {
            if (!$this->couldHoldIncluded($excl[$i])) {
                $this->scandir($this->basedir . $excl[$i], $excl[$i] . DIRECTORY_SEPARATOR, false);
            }
        }

        for ($i = 0, $_i = count($notIncl); $i < $_i; $i++) {
            if (!$this->couldHoldIncluded($notIncl[$i])) {
                $this->scandir($this->basedir . $notIncl[$i], $notIncl[$i] . DIRECTORY_SEPARATOR, false);
            }
        }

        $this->haveSlowResults = true;
    }

    /**
     * Lists contens of a given directory and returns array with entries
     *
     * @param   src String. Source path and name file to copy.
     *
     * @return array directory entries
     * @author  Albert Lash, alash@plateauinnovation.com
     */

    public function listDir($_dir)
    {
        $d = dir($_dir);
        $list = array();
        while (($entry = $d->read()) !== false) {
            if ($entry != "." && $entry != "..") {
                $list[] = $entry;
            }
        }
        $d->close();

        return $list;
    }

    /**
     * Scans the passed dir for files and directories. Found files and
     * directories are placed in their respective collections, based on the
     * matching of includes and excludes. When a directory is found, it is
     * scanned recursively.
     *
     * @param $_rootdir
     * @param $_vpath
     * @param $_fast
     * @internal param the $dir directory to scan
     * @internal param the $vpath path relative to the basedir (needed to prevent
     *              problems with an absolute path when using dir)
     *
     * @see #filesIncluded
     * @see #filesNotIncluded
     * @see #filesExcluded
     * @see #dirsIncluded
     * @see #dirsNotIncluded
     * @see #dirsExcluded
     */
    private function scandir($_rootdir, $_vpath, $_fast)
    {

        if (!is_readable($_rootdir)) {
            return;
        }

        $newfiles = self::listDir($_rootdir);

        for ($i = 0, $_i = count($newfiles); $i < $_i; $i++) {

            $file = $_rootdir . DIRECTORY_SEPARATOR . $newfiles[$i];
            $name = $_vpath . $newfiles[$i];

            if (@is_link($file) && !$this->expandSymbolicLinks) {
                if ($this->isIncluded($name)) {
                    if (!$this->isExcluded($name)) {
                        if ($this->isSelected($name, $file)) {
                            $this->filesIncluded[] = $name;
                        } else {
                            $this->everythingIncluded = false;
                            $this->filesDeselected[] = $name;
                        }
                    } else {
                        $this->everythingIncluded = false;
                        $this->filesExcluded[] = $name;
                    }
                } else {
                    $this->everythingIncluded = false;
                    $this->filesNotIncluded[] = $name;
                }
            } else {
                if (@is_dir($file)) {
                    if ($this->isIncluded($name)) {
                        if (!$this->isExcluded($name)) {
                            if ($this->isSelected($name, $file)) {
                                $this->dirsIncluded[] = $name;
                                if ($_fast) {
                                    $this->scandir($file, $name . DIRECTORY_SEPARATOR, $_fast);
                                }
                            } else {
                                $this->everythingIncluded = false;
                                $this->dirsDeselected[] = $name;
                                if ($_fast && $this->couldHoldIncluded($name)) {
                                    $this->scandir($file, $name . DIRECTORY_SEPARATOR, $_fast);
                                }
                            }
                        } else {
                            $this->everythingIncluded = false;
                            $this->dirsExcluded[] = $name;
                            if ($_fast && $this->couldHoldIncluded($name)) {
                                $this->scandir($file, $name . DIRECTORY_SEPARATOR, $_fast);
                            }
                        }
                    } else {
                        $this->everythingIncluded = false;
                        $this->dirsNotIncluded[] = $name;
                        if ($_fast && $this->couldHoldIncluded($name)) {
                            $this->scandir($file, $name . DIRECTORY_SEPARATOR, $_fast);
                        }
                    }

                    if (!$_fast) {
                        $this->scandir($file, $name . DIRECTORY_SEPARATOR, $_fast);
                    }

                } elseif (@is_file($file)) {
                    if ($this->isIncluded($name)) {
                        if (!$this->isExcluded($name)) {
                            if ($this->isSelected($name, $file)) {
                                $this->filesIncluded[] = $name;
                            } else {
                                $this->everythingIncluded = false;
                                $this->filesDeselected[] = $name;
                            }
                        } else {
                            $this->everythingIncluded = false;
                            $this->filesExcluded[] = $name;
                        }
                    } else {
                        $this->everythingIncluded = false;
                        $this->filesNotIncluded[] = $name;
                    }
                }
            }
        }
    }

    /**
     * Tests whether a name matches against at least one include pattern.
     *
     * @param $_name
     * @internal param the $name name to match
     * @return bool <code>true</code> when the name matches against at least one
     */
    protected function isIncluded($_name)
    {
        for ($i = 0, $_i = count($this->includes); $i < $_i; $i++) {
            if (DirectoryScanner::matchPath($this->includes[$i], $_name, $this->isCaseSensitive)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Tests whether a name matches the start of at least one include pattern.
     *
     * @param string $_name the name to match
     * @return bool <code>true</code> when the name matches against at least one
     *                           include pattern, <code>false</code> otherwise.
     */
    protected function couldHoldIncluded($_name)
    {
        for ($i = 0; $i < count($this->includes); $i++) {
            if (DirectoryScanner::matchPatternStart($this->includes[$i], $_name, $this->isCaseSensitive)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Tests whether a name matches against at least one exclude pattern.
     *
     * @param string $_name the name to match
     * @return bool <code>true</code> when the name matches against at least one
     *                           exclude pattern, <code>false</code> otherwise.
     */
    protected function isExcluded($_name)
    {
        for ($i = 0; $i < count($this->excludes); $i++) {
            if (DirectoryScanner::matchPath($this->excludes[$i], $_name, $this->isCaseSensitive)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Get the names of the files that matched at least one of the include
     * patterns, and matched none of the exclude patterns.
     * The names are relative to the basedir.
     *
     * @return the names of the files
     */
    public function getIncludedFiles()
    {
        return $this->filesIncluded;
    }

    /**
     * Get the names of the files that matched at none of the include patterns.
     * The names are relative to the basedir.
     *
     * @return the names of the files
     */
    public function getNotIncludedFiles()
    {
        $this->slowScan();

        return $this->filesNotIncluded;
    }

    /**
     * Get the names of the files that matched at least one of the include
     * patterns, an matched also at least one of the exclude patterns.
     * The names are relative to the basedir.
     *
     * @return the names of the files
     */

    public function getExcludedFiles()
    {
        $this->slowScan();

        return $this->filesExcluded;
    }

    /**
     * <p>Returns the names of the files which were selected out and
     * therefore not ultimately included.</p>
     *
     * <p>The names are relative to the base directory. This involves
     * performing a slow scan if one has not already been completed.</p>
     *
     * @return the names of the files which were deselected.
     *
     * @see #slowScan
     */
    public function getDeselectedFiles()
    {
        $this->slowScan();

        return $this->filesDeselected;
    }

    /**
     * Get the names of the directories that matched at least one of the include
     * patterns, an matched none of the exclude patterns.
     * The names are relative to the basedir.
     *
     * @return the names of the directories
     */

    public function getIncludedDirectories()
    {
        return $this->dirsIncluded;
    }

    /**
     * Get the names of the directories that matched at none of the include
     * patterns.
     * The names are relative to the basedir.
     *
     * @return the names of the directories
     */
    public function getNotIncludedDirectories()
    {
        $this->slowScan();

        return $this->dirsNotIncluded;
    }

    /**
     * <p>Returns the names of the directories which were selected out and
     * therefore not ultimately included.</p>
     *
     * <p>The names are relative to the base directory. This involves
     * performing a slow scan if one has not already been completed.</p>
     *
     * @return the names of the directories which were deselected.
     *
     * @see #slowScan
     */
    public function getDeselectedDirectories()
    {
        $this->slowScan();

        return $this->dirsDeselected;
    }

    /**
     * Get the names of the directories that matched at least one of the include
     * patterns, an matched also at least one of the exclude patterns.
     * The names are relative to the basedir.
     *
     * @return the names of the directories
     */
    public function getExcludedDirectories()
    {
        $this->slowScan();

        return $this->dirsExcluded;
    }

    /**
     * Adds the array with default exclusions to the current exclusions set.
     *
     */
    public function addDefaultExcludes()
    {
        //$excludesLength = ($this->excludes == null) ? 0 : count($this->excludes);
        foreach ($this->DEFAULTEXCLUDES as $pattern) {
            $pattern = str_replace('\\', DIRECTORY_SEPARATOR, $pattern);
            $pattern = str_replace('/', DIRECTORY_SEPARATOR, $pattern);
            $this->excludes[] = $pattern;
        }
    }

    /**
     * Sets the selectors that will select the filelist.
     *
     * @param specifies $selectors
     * @internal param specifies $selectors the selectors to be invoked on a scan
     */
    public function setSelectors($selectors)
    {
        $this->selectors = $selectors;
    }

    /**
     * Returns whether or not the scanner has included all the files or
     * directories it has come across so far.
     *
     * @return bool <code>true</code> if all files and directories which have
     */
    public function isEverythingIncluded()
    {
        return $this->everythingIncluded;
    }

    /**
     * Tests whether a name should be selected.
     *
     * @param  string  $name The filename to check for selecting.
     * @param  string  $file The full file path.
     * @return boolean False when the selectors says that the file
     *                      should not be selected, True otherwise.
     */
    protected function isSelected($name, $file)
    {
        if ($this->selectors !== null) {
            $basedir = new PhingFile($this->basedir);
            $file = new PhingFile($file);
            if (!$file->canRead()) {
                return false;
            }

            foreach ($this->selectors as $selector) {
                if (!$selector->isSelected($basedir, $name, $file)) {
                    return false;
                }
            }
        }

        return true;
    }

}
<?php

include_once 'phing/system/io/PhingFile.php';

/**
 * $Id: 9f3deadd6a2b93620d27fe8959f9f688b4c58171 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Extended file stream wrapper class which auto-creates directories
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 9f3deadd6a2b93620d27fe8959f9f688b4c58171 $
 * @package phing.util
 */
class ExtendedFileStream
{
    private $fp = null;

    public static function registerStream()
    {
        if (!in_array("efile", stream_get_wrappers())) {
            stream_wrapper_register("efile", "ExtendedFileStream");
        }
    }

    public static function unregisterStream()
    {
        stream_wrapper_unregister("efile");
    }

    /**
     * @param $path
     */
    private function createDirectories($path)
    {
        $f = new PhingFile($path);
        if (!$f->exists()) {
            $f->mkdirs();
        }
    }

    /**
     * @param $path
     * @param $mode
     * @param $options
     * @param $opened_path
     * @return bool
     * @throws IOException
     */
    public function stream_open($path, $mode, $options, &$opened_path)
    {
        // if we're on Windows, urldecode() the path again
        if (FileSystem::getFileSystem()->getSeparator() == '\\') {
            $path = urldecode($path);
        }

        $filepath = substr($path, 8);

        $this->createDirectories(dirname($filepath));

        $this->fp = fopen($filepath, $mode);

        if (! $this->fp) {
            throw new BuildException("Unable to open stream for path {$path}");
        }

        return true;
    }

    public function stream_close()
    {
        fclose($this->fp);
        $this->fp = null;
    }

    /**
     * @param $count
     * @return string
     */
    public function stream_read($count)
    {
        return fread($this->fp, $count);
    }

    /**
     * @param $data
     * @return int
     */
    public function stream_write($data)
    {
        return fwrite($this->fp, $data);
    }

    /**
     * @return bool
     */
    public function stream_eof()
    {
        return feof($this->fp);
    }

    /**
     * @return int
     */
    public function stream_tell()
    {
        return ftell($this->fp);
    }

    /**
     * @param $offset
     * @param $whence
     * @return int
     */
    public function stream_seek($offset, $whence)
    {
        return fseek($this->fp, $offset, $whence);
    }

    /**
     * @return bool
     */
    public function stream_flush()
    {
        return fflush($this->fp);
    }

    /**
     * @return array
     */
    public function stream_stat()
    {
        return fstat($this->fp);
    }

    /**
     * @param $path
     * @return bool
     */
    public function unlink($path)
    {
        return false;
    }

    /**
     * @param $path_from
     * @param $path_to
     * @return bool
     */
    public function rename($path_from, $path_to)
    {
        return false;
    }

    /**
     * @param $path
     * @param $mode
     * @param $options
     * @return bool
     */
    public function mkdir($path, $mode, $options)
    {
        return false;
    }

    /**
     * @param $path
     * @param $options
     * @return bool
     */
    public function rmdir($path, $options)
    {
        return false;
    }
}

;
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/system/lang/Character.php';
include_once 'phing/util/StringHelper.php';
include_once 'phing/system/io/BufferedReader.php';
include_once 'phing/system/io/BufferedWriter.php';
include_once 'phing/filters/util/ChainReaderHelper.php';
include_once 'phing/system/io/PhingFile.php';

/**
 * File utility class.
 * - handles os independent stuff etc
 * - mapper stuff
 * - filter stuff
 *
 * @package  phing.util
 */
class FileUtils
{
    /**
     * Returns the default file/dir creation mask value
     * (The mask value is prepared w.r.t the current user's file-creation mask value)
     *
     * @param  boolean $dirmode     Directory creation mask to select
     * @param  boolean $returnoctal Whether the return value is in octal representation
     *
     * @return string  Creation Mask
     */
    public static function getDefaultFileCreationMask($dirmode = false, $returnoctal = false)
    {

        // Preparing the creation mask base permission
        $permission = ($dirmode === true) ? 0777 : 0666;

        // Default mask information
        $defaultmask = sprintf('%03o', ($permission & ($permission - (int) sprintf('%04o', umask()))));

        return ($returnoctal ? octdec($defaultmask) : $defaultmask);
    }

    /**
     * Returns a new Reader with filterchains applied.  If filterchains are empty,
     * simply returns passed reader.
     *
     * @param  Reader  $in            Reader to modify (if appropriate).
     * @param  array   &$filterChains filter chains to apply.
     * @param  Project $project
     * @return Reader  Assembled Reader (w/ filter chains).
     */
    public static function getChainedReader(Reader $in, &$filterChains, Project $project)
    {
        if (!empty($filterChains)) {
            $crh = new ChainReaderHelper();
            $crh->setBufferSize(65536); // 64k buffer, but isn't being used (yet?)
            $crh->setPrimaryReader($in);
            $crh->setFilterChains($filterChains);
            $crh->setProject($project);
            $rdr = $crh->getAssembledReader();

            return $rdr;
        } else {
            return $in;
        }
    }

    /**
     * Copies a file using filter chains.
     *
     * @param  PhingFile $sourceFile
     * @param  PhingFile $destFile
     * @param  boolean $overwrite
     * @param  boolean $preserveLastModified
     * @param  array $filterChains
     * @param  Project $project
     * @param  integer $mode
     * @param bool $preservePermissions
     * @throws Exception
     * @throws IOException
     * @return void
     */
    public function copyFile(
        PhingFile $sourceFile,
        PhingFile $destFile,
        $overwrite = false,
        $preserveLastModified = true,
        &$filterChains = null,
        Project $project,
        $mode = 0755,
        $preservePermissions = true
    ) {

        if ($overwrite || !$destFile->exists() || $destFile->lastModified() < $sourceFile->lastModified()) {
            if ($destFile->exists() && $destFile->isFile()) {
                $destFile->delete();
            }

            // ensure that parent dir of dest file exists!
            $parent = $destFile->getParentFile();
            if ($parent !== null && !$parent->exists()) {

                // Setting source directory permissions to target
                // (On permissions preservation, the target directory permissions
                // will be inherited from the source directory, otherwise the 'mode'
                // will be used)
                $dirMode = ($preservePermissions ? $sourceFile->getParentFile()->getMode() : $mode);

                $parent->mkdirs($dirMode);
            }

            if ((is_array($filterChains)) && (!empty($filterChains))) {

                $in = self::getChainedReader(new BufferedReader(new FileReader($sourceFile)), $filterChains, $project);
                $out = new BufferedWriter(new FileWriter($destFile));

                // New read() methods returns a big buffer.
                while (-1 !== ($buffer = $in->read())) { // -1 indicates EOF
                    $out->write($buffer);
                }

                if ($in !== null) {
                    $in->close();
                }
                if ($out !== null) {
                    $out->close();
                }

                // Set/Copy the permissions on the target
                if ($preservePermissions === true) {
                    $destFile->setMode($sourceFile->getMode());
                }

            } else {
                // simple copy (no filtering)
                $sourceFile->copyTo($destFile);

                // By default, PHP::Copy also copies the file permissions. Therefore,
                // re-setting the mode with the "user file-creation mask" information.
                if ($preservePermissions === false) {
                    $destFile->setMode(FileUtils::getDefaultFileCreationMask(false, true));
                }
            }

            if ($preserveLastModified && !$destFile->isLink()) {
                $destFile->setLastModified($sourceFile->lastModified());
            }

        }
    }

    /**
     * Interpret the filename as a file relative to the given file -
     * unless the filename already represents an absolute filename.
     *
     * @param  PhingFile $file the "reference" file for relative paths. This
     *         instance must be an absolute file and must not contain
     *         ./ or ../ sequences (same for \ instead of /).
     * @param  string $filename a file name
     *
     * @throws IOException
     *
     * @return PhingFile A PhingFile object pointing to an absolute file that doesn't contain ./ or ../ sequences
     *                   and uses the correct separator for the current platform.
     */
    public function resolveFile($file, $filename)
    {
        // remove this and use the static class constant File::separator
        // as soon as ZE2 is ready
        $fs = FileSystem::getFileSystem();

        $filename = str_replace('/', $fs->getSeparator(), str_replace('\\', $fs->getSeparator(), $filename));

        // deal with absolute files
        if (StringHelper::startsWith($fs->getSeparator(), $filename) ||
            (strlen($filename) >= 2 && Character::isLetter($filename{0}) && $filename{1} === ':')
        ) {
            return new PhingFile($this->normalize($filename));
        }

        if (strlen($filename) >= 2 && Character::isLetter($filename{0}) && $filename{1} === ':') {
            return new PhingFile($this->normalize($filename));
        }

        $helpFile = new PhingFile($file->getAbsolutePath());

        $tok = strtok($filename, $fs->getSeparator());
        while ($tok !== false) {
            $part = $tok;
            if ($part === '..') {
                $parentFile = $helpFile->getParent();
                if ($parentFile === null) {
                    $msg = "The file or path you specified ($filename) is invalid relative to " . $file->getPath();
                    throw new IOException($msg);
                }
                $helpFile = new PhingFile($parentFile);
            } else {
                if ($part === '.') {
                    // Do nothing here
                } else {
                    $helpFile = new PhingFile($helpFile, $part);
                }
            }
            $tok = strtok($fs->getSeparator());
        }

        return new PhingFile($helpFile->getAbsolutePath());
    }

    /**
     * Normalize the given absolute path.
     *
     * This includes:
     *   - Uppercase the drive letter if there is one.
     *   - Remove redundant slashes after the drive spec.
     *   - resolve all ./, .\, ../ and ..\ sequences.
     *   - DOS style paths that start with a drive letter will have
     *     \ as the separator.
     *
     * @param  string $path Path to normalize.
     *
     * @throws IOException
     *
     * @return string
     */
    public function normalize($path)
    {

        $path = (string) $path;
        $orig = $path;

        $path = str_replace('/', DIRECTORY_SEPARATOR, str_replace('\\', DIRECTORY_SEPARATOR, $path));

        // make sure we are dealing with an absolute path
        if (!StringHelper::startsWith(DIRECTORY_SEPARATOR, $path)
            && !(strlen($path) >= 2 && Character::isLetter($path{0}) && $path{1} === ':')
        ) {
            throw new IOException("$path is not an absolute path");
        }

        $dosWithDrive = false;
        $root = null;

        // Eliminate consecutive slashes after the drive spec

        if (strlen($path) >= 2 && Character::isLetter($path{0}) && $path{1} === ':') {
            $dosWithDrive = true;

            $ca = str_replace('/', '\\', $path);
            $ca = StringHelper::toCharArray($ca);

            $path = strtoupper($ca[0]) . ':';

            for ($i = 2, $_i = count($ca); $i < $_i; $i++) {
                if (($ca[$i] !== '\\') ||
                    ($ca[$i] === '\\' && $ca[$i - 1] !== '\\')
                ) {
                    $path .= $ca[$i];
                }
            }

            $path = str_replace('\\', DIRECTORY_SEPARATOR, $path);

            if (strlen($path) == 2) {
                $root = $path;
                $path = "";
            } else {
                $root = substr($path, 0, 3);
                $path = substr($path, 3);
            }

        } else {
            if (strlen($path) == 1) {
                $root = DIRECTORY_SEPARATOR;
                $path = "";
            } else {
                if ($path{1} == DIRECTORY_SEPARATOR) {
                    // UNC drive
                    $root = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
                    $path = substr($path, 2);
                } else {
                    $root = DIRECTORY_SEPARATOR;
                    $path = substr($path, 1);
                }
            }
        }

        $s = array();
        array_push($s, $root);
        $tok = strtok($path, DIRECTORY_SEPARATOR);
        while ($tok !== false) {
            $thisToken = $tok;
            if ("." === $thisToken) {
                $tok = strtok(DIRECTORY_SEPARATOR);
                continue;
            } elseif (".." === $thisToken) {
                if (count($s) < 2) {
                    // using '..' in path that is too short
                    throw new IOException("Cannot resolve path: $orig");
                } else {
                    array_pop($s);
                }
            } else { // plain component
                array_push($s, $thisToken);
            }
            $tok = strtok(DIRECTORY_SEPARATOR);
        }

        $sb = "";
        for ($i = 0, $_i = count($s); $i < $_i; $i++) {
            if ($i > 1) {
                // not before the filesystem root and not after it, since root
                // already contains one
                $sb .= DIRECTORY_SEPARATOR;
            }
            $sb .= (string) $s[$i];
        }


        $path = (string) $sb;
        if ($dosWithDrive === true) {
            $path = str_replace('/', '\\', $path);
        }

        return $path;
    }

    /**
     * Create a temporary file in a given directory.
     *
     * <p>The file denoted by the returned abstract pathname did not
     * exist before this method was invoked, any subsequent invocation
     * of this method will yield a different file name.</p>
     *
     * @param string $prefix        prefix before the random number.
     * @param string $suffix        file extension; include the '.'.
     * @param PhingFile $parentDir  Directory to create the temporary file in;
     *                              sys_get_temp_dir() used if not specified.
     * @param boolean $deleteOnExit whether to set the tempfile for deletion on
     *                              normal exit.
     * @param boolean $createFile   true if the file must actually be created. If false
     *                              chances exist that a file with the same name is created in the time
     *                              between invoking this method and the moment the file is actually created.
     *                              If possible set to true.
     * @return PhingFile            a File reference to the new temporary file.
     * @throws BuildException
     */
    public function createTempFile($prefix, $suffix, PhingFile $parentDir, $deleteOnExit = false, $createFile = false)
    {
        $result = null;
        $parent = ($parentDir === null) ? sys_get_temp_dir() : $parentDir->getPath();

        if ($createFile) {
            try {
                $result = PhingFile::createTempFile($prefix, $suffix, new PhingFile($parent));
            } catch (IOException $e) {
                throw new BuildException("Could not create tempfile in " . $parent, $e);
            }
        } else {
            do {
                $result = new PhingFile($parent, $prefix . substr(md5(time()), 0, 8) . $suffix);
            } while ($result->exists());
        }

        if ($deleteOnExit) {
            $result->deleteOnExit();
        }

        return $result;
    }

    /**
     * @param PhingFile $file1
     * @param PhingFile $file2
     *
     * @return boolean Whether contents of two files is the same.
     */
    public function contentEquals(PhingFile $file1, PhingFile $file2)
    {

        if (!($file1->exists() || $file2->exists())) {
            return false;
        }

        if (!($file1->canRead() || $file2->canRead())) {
            return false;
        }

        $c1 = file_get_contents($file1->getAbsolutePath());
        $c2 = file_get_contents($file2->getAbsolutePath());

        return trim($c1) == trim($c2);
    }
}
<?php

/**
 * $Id: 7f4acf8203b01fce283e1a066159aa51146b6831 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/system/io/Writer.php';
require_once 'phing/Task.php';

/**
 * Extends the Writer class to output messages to Phing's log
 *
 * @author Michiel Rook <mrook@php.net>
 * @version $Id: 7f4acf8203b01fce283e1a066159aa51146b6831 $
 * @package phing.util
 */
class LogWriter extends Writer
{
    private $task = null;

    private $level = null;

    /**
     * Constructs a new LogWriter object
     * @param Task $task
     * @param int $level
     */
    public function __construct(Task $task, $level = Project::MSG_INFO)
    {
        $this->task = $task;
        $this->level = $level;
    }

    /**
     * @see Writer::write()
     * @param string $buf
     * @param null $off
     * @param null $len
     */
    public function write($buf, $off = null, $len = null)
    {
        $lines = explode("\n", $buf);

        foreach ($lines as $line) {
            if ($line == "") {
                continue;
            }

            $this->task->log($line, $this->level);
        }
    }

    /**
     * @see Writer::reset()
     */
    public function reset()
    {
    }

    /**
     * @see Writer::close()
     */
    public function close()
    {
    }

    /**
     * @see Writer::open()
     */
    public function open()
    {
    }

    /**
     * @see Writer::getResource()
     */
    public function getResource()
    {
        return $this->task;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

include_once 'phing/util/StringHelper.php';

/**
 * A Path tokenizer takes a path and returns the components that make up
 * that path.
 *
 * The path can use path separators of either ':' or ';' and file separators
 * of either '/' or '\'.
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Conor MacNeill (Ant)
 * @author Jeff Tulley <jtulley@novell.com>  (Ant)
 *
 * @package phing.util
 */
class PathTokenizer
{
    /**
     * A array of tokens, created by preg_split().
     */
    private $tokens = array();

    /**
     * A string which stores any path components which have been read ahead
     * due to DOS filesystem compensation.
     * @var string
     */
    private $lookahead;

    /**
     * Flag to indicate whether or not we are running on a platform with a
     * DOS style filesystem
     * @var boolean
     */
    private $dosStyleFilesystem;


    /**
     * Constructs a path tokenizer for the specified path.
     *
     * @param string $path The path to tokenize. Must not be <code>null</code>.
     */
    public function __construct($path)
    {

        // on Windows and Unix, we can ignore delimiters and still have

        // enough information to tokenize correctly.

        $this->tokens = preg_split("/[;:]/", $path, -1, PREG_SPLIT_NO_EMPTY);

        $this->dosStyleFilesystem = (PATH_SEPARATOR == ';');

    }


    /**
     * Tests if there are more path elements available from this tokenizer's
     * path. If this method returns <code>true</code>, then a subsequent call
     * to nextToken will successfully return a token.
     *
     * @return bool <code>true</code> if and only if there is at least one token
     *                                in the string after the current position; <code>false</code> otherwise.
     */
    public function hasMoreTokens()
    {

        if ($this->lookahead !== null) {
            return true;

        }

        return !empty($this->tokens);
    }

    /**
     * Returns the next path element from this tokenizer.
     *
     * @return string the next path element from this tokenizer.
     *
     * @throws Exception if there are no more elements in this tokenizer's path.
     */
    public function nextToken()
    {
        if ($this->lookahead !== null) {

            $token = $this->lookahead;

            $this->lookahead = null;

        } else {

            $token = trim(array_shift($this->tokens));

        }


        if (strlen($token) === 1 && Character::isLetter($token{0})

            && $this->dosStyleFilesystem

            && !empty($this->tokens)
        ) {

            // we are on a dos style system so this path could be a drive

            // spec. We look at the next token

            $nextToken = trim(array_shift($this->tokens));

            if (StringHelper::startsWith('\\', $nextToken) || StringHelper::startsWith('/', $nextToken)) {

                // we know we are on a DOS style platform and the next path

                // starts with a slash or backslash, so we know this is a

                // drive spec

                $token .= ':' . $nextToken;

            } else {

                // store the token just read for next time

                $this->lookahead = $nextToken;

            }

        }

        return $token;
    }

    /**
     * Non StringTokenizer function, that indicates whether the specified path is contained in loaded tokens.
     * We can do this easily because in PHP implimentation we're using arrays.
     *
     * @param  string  $path path to search for.
     *
     * @return boolean
     */
    public function contains($path)
    {
        return in_array($path, $this->tokens, true);
    }
}
<?php
/**
 * Part of phing, the PHP build tool
 *
 * PHP version 5
 *
 * @category Util
 * @package  phing.util
 * @author   Christian Weiske <cweiske@cweiske.de>
 * @license  LGPL v3 or later http://www.gnu.org/licenses/lgpl.html
 * @version  SVN: $Id: 36ea68a722ee20d394476949c62c276bb45cd316 $
 * @link     http://www.phing.info/
 */
require_once 'phing/util/DirectoryScanner.php';

/**
 * Scans for files in a PEAR package.
 *
 * @category Util
 * @package  phing.util
 * @author   Christian Weiske <cweiske@cweiske.de>
 * @license  LGPL v3 or later http://www.gnu.org/licenses/lgpl.html
 * @link     http://www.phing.info/
 */
class PearPackageScanner extends DirectoryScanner
{
    protected $packageInfo;
    protected $role = 'php';
    protected $config;
    protected $package;
    protected $channel = 'pear.php.net';
    protected $packageFile;

    /**
     * Load PEAR_Config and PEAR_PackageFile
     */
    public function __construct()
    {
        @include_once 'PEAR/Config.php';
        @include_once 'PEAR/PackageFile.php';

        if (!class_exists('PEAR_Config')) {
            throw new BuildException(__CLASS__ . " requires PEAR to be installed");
        }
    }

    /**
     * Sets the package.xml file to read, instead of using the
     * local pear installation.
     *
     * @param string $descfile Name of package xml file
     *
     * @throws BuildException
     * @return void
     */
    public function setDescFile($descfile)
    {
        if ($descfile != '' && !file_exists($descfile)) {
            throw new BuildException(
                'PEAR package xml file "' . $descfile . '" does not exist'
            );
        }

        $this->packageFile = $descfile;
    }

    /**
     * Sets the name of the PEAR package to get the files from
     *
     * @param string $package Package name without channel
     *
     * @return void
     */
    public function setPackage($package)
    {
        $this->package = $package;
    }

    /**
     * Sets the name of the package channel name
     *
     * @param string $channel package channel name or alias
     *
     * @return void
     */
    public function setChannel($channel)
    {
        $this->channel = $channel;
    }

    /**
     * Sets the full path to the PEAR configuration file
     *
     * @param string $config Configuration file
     *
     * @throws BuildException
     * @return void
     */
    public function setConfig($config)
    {
        if ($config != '') {
            if (!file_exists($config)) {
                throw new BuildException(
                    'PEAR configuration file "' . $config . '" does not exist'
                );
            }
        } else {
            // try to auto-detect a pear local installation
            if (DIRECTORY_SEPARATOR == '/') {
                $config = '.pearrc';
            } else {
                $config = 'pear.ini';
            }
            $config = PEAR_CONFIG_DEFAULT_BIN_DIR . DIRECTORY_SEPARATOR . $config;
            if (!file_exists($config)) {
                // cannot find a pear local installation
                $config = '';
            }
        }

        $this->config = $config;
    }

    /**
     * Sets the role of files that should be included.
     * Examples are php,doc,script
     *
     * @param string $role PEAR file role
     *
     * @throws BuildException
     * @return void
     *
     * @internal
     * We do not verify the role against a hardcoded list since that
     * would break packages with additional roles.
     */
    public function setRole($role)
    {
        if ($role == '' && $this->packageFile == '') {
            throw new BuildException('A non-empty role is required');
        }

        $this->role = $role;
    }

    /**
     * Loads the package information.
     *
     * @return void
     *
     * @uses $packageInfo
     */
    protected function init()
    {
        if (!$this->packageInfo) {
            $this->packageInfo = $this->loadPackageInfo();
        }
    }

    /**
     * Loads and returns the PEAR package information.
     *
     * @return PEAR_PackageFile_v2 Package information object
     *
     * @throws BuildException When the package does not exist
     */
    protected function loadPackageInfo()
    {
        $config = PEAR_Config::singleton($this->config);

        if (empty($this->packageFile)) {
            // loads informations from PEAR package installed
            $reg = $config->getRegistry();
            if (!$reg->packageExists($this->package, $this->channel)) {
                throw new BuildException(
                    sprintf(
                        'PEAR package %s/%s does not exist',
                        $this->channel,
                        $this->package
                    )
                );
            }
            $packageInfo = $reg->getPackage($this->package, $this->channel);
        } else {
            // loads informations from PEAR package XML description file
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $pkg = new PEAR_PackageFile($config);
            $packageInfo = $pkg->fromPackageFile($this->packageFile, PEAR_VALIDATE_NORMAL);
            PEAR::staticPopErrorHandling();
            if (@PEAR::isError($packageInfo)) {
                throw new BuildException("Errors in package file: " . $packageInfo->getMessage());
            }
        }

        return $packageInfo;
    }

    /**
     * Generates the list of included files and directories
     *
     * @return boolean True if all went well, false if something was wrong
     *
     * @uses $filesIncluded
     * @uses $filesDeselected
     * @uses $filesNotIncluded
     * @uses $filesExcluded
     * @uses $everythingIncluded
     * @uses $dirsIncluded
     * @uses $dirsDeselected
     * @uses $dirsNotIncluded
     * @uses $dirsExcluded
     */
    public function scan()
    {
        $this->init();
        $list = $this->packageInfo->getFilelist();

        if ($this->includes === null) {
            // No includes supplied, so set it to 'matches all'
            $this->includes = array("**");
        }
        if ($this->excludes === null) {
            $this->excludes = array();
        }

        $this->filesIncluded = array();
        $this->filesNotIncluded = array();
        $this->filesExcluded = array();
        $this->filesDeselected = array();

        $this->dirsIncluded = array();
        $this->dirsNotIncluded = array();
        $this->dirsExcluded = array();
        $this->dirsDeselected = array();
        $origFirstFile = null;

        foreach ($list as $file => $att) {
            if ($att['role'] != $this->role && $this->role != '') {
                continue;
            }
            $origFile = $file;
            if (isset($att['install-as'])) {
                $file = $att['install-as'];
            } else {
                if (isset($att['baseinstalldir'])) {
                    $file = ltrim($att['baseinstalldir'] . '/' . $file, '/');
                }
            }
            $file = str_replace('/', DIRECTORY_SEPARATOR, $file);

            if ($this->isIncluded($file)) {

                if ($this->isExcluded($file)) {
                    $this->everythingIncluded = false;
                    if (@is_dir($file)) {
                        $this->dirsExcluded[] = $file;
                    } else {
                        $this->filesExcluded[] = $file;
                    }
                } else {
                    if (@is_dir($file)) {
                        $this->dirsIncluded[] = $file;
                    } else {
                        $this->filesIncluded[] = $file;
                        if ($origFirstFile === null) {
                            $origFirstFile = $origFile;
                        }
                    }
                }
            } else {
                $this->everythingIncluded = false;
                if (@is_dir($file)) {
                    $this->dirsNotIncluded[] = $file;
                } else {
                    $this->filesNotIncluded[] = $file;
                }
            }
        }

        if (count($this->filesIncluded) > 0) {
            if (empty($this->packageFile)) {
                $att = $list[$origFirstFile];
                $base_dir = substr(
                    $att['installed_as'],
                    0,
                    -strlen($this->filesIncluded[0])
                );
            } else {
                $base_dir = dirname($this->packageFile);
            }
            $this->setBaseDir($base_dir);
        }

        return true;
    }

}
<?php
/*
 *  $Id: fcea2d1ae635e834c061996c46d721b780532ed9 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

require_once 'phing/util/regexp/RegexpEngine.php';

/**
 * PREG Regexp Engine.
 * Implements a regexp engine using PHP's preg_match(), preg_match_all(), and preg_replace() functions.
 *
 * @author hans lellelid, hans@velum.net
 * @package phing.util.regexp
 */
class PregEngine implements RegexpEngine
{
    /**
     * Set to null by default to distinguish between false and not set
     * @var boolean
     */
    private $ignoreCase = null;

    /**
     * Set to null by default to distinguish between false and not set
     * @var boolean
     */
    private $multiline = null;

    /**
     * Pattern modifiers
     * @link http://php.net/manual/en/reference.pcre.pattern.modifiers.php
     * @var string
     */
    private $modifiers = null;

    /**
     * Set the limit.
     * @var int
     */
    private $limit = -1;

    /**
     * Pattern delimiter.
     */
    const DELIMITER = '`';

    /**
     * Sets pattern modifiers for regex engine
     *
     * @param  string $mods Modifiers to be applied to a given regex
     * @return void
     */
    public function setModifiers($mods)
    {
        $this->modifiers = (string) $mods;
    }

    /**
     * Gets pattern modifiers.
     * @return string
     */
    public function getModifiers()
    {
        $mods = $this->modifiers;
        if ($this->getIgnoreCase()) {
            $mods .= 'i';
        } elseif ($this->getIgnoreCase() === false) {
            $mods = str_replace('i', '', $mods);
        }
        if ($this->getMultiline()) {
            $mods .= 's';
        } elseif ($this->getMultiline() === false) {
            $mods = str_replace('s', '', $mods);
        }
        // filter out duplicates
        $mods = preg_split('//', $mods, -1, PREG_SPLIT_NO_EMPTY);
        $mods = implode('', array_unique($mods));

        return $mods;
    }

    /**
     * Sets whether or not regex operation is case sensitive.
     * @param  boolean $bit
     * @return void
     */
    public function setIgnoreCase($bit)
    {
        $this->ignoreCase = (boolean) $bit;
    }

    /**
     * Gets whether or not regex operation is case sensitive.
     * @return boolean
     */
    public function getIgnoreCase()
    {
        return $this->ignoreCase;
    }

    /**
     * Sets whether regexp should be applied in multiline mode.
     * @param boolean $bit
     */
    public function setMultiline($bit)
    {
        $this->multiline = $bit;
    }

    /**
     * Gets whether regexp is to be applied in multiline mode.
     * @return boolean
     */
    public function getMultiline()
    {
        return $this->multiline;
    }

    /**
     * Sets the maximum possible replacements for each pattern.
     * @param int $limit
     */
    public function setLimit($limit)
    {
        $this->limit = $limit;
    }

    /**
     * Returns the maximum possible replacements for each pattern.
     * @return int
     */
    public function getLimit()
    {
        return $this->limit;
    }

    /**
     * The pattern needs to be converted into PREG style -- which includes adding expression delims & any flags, etc.
     * @param  string $pattern
     * @return string prepared pattern.
     */
    private function preparePattern($pattern)
    {
        $delimiterPattern = '/\\\\*' . self::DELIMITER . '/';

        // The following block escapes usages of the delimiter in the pattern if it's not already escaped.
        if (preg_match_all($delimiterPattern, $pattern, $matches, PREG_OFFSET_CAPTURE)) {
            $diffOffset = 0;

            foreach ($matches[0] as $match) {
                $str = $match[0];
                $offset = $match[1]+$diffOffset;

                $escStr = (strlen($str) % 2) ? '\\'.$str : $str; // This will increase an even number of backslashes, before a forward slash, to an odd number.  I.e. '\\/' becomes '\\\/'.

                $diffOffset += strlen($escStr)-strlen($str);

                $pattern = substr_replace($pattern, $escStr, $offset, strlen($str));
            }
        }

        return self::DELIMITER . $pattern . self::DELIMITER . $this->getModifiers();
    }

    /**
     * Matches pattern against source string and sets the matches array.
     * @param  string  $pattern The regex pattern to match.
     * @param  string  $source  The source string.
     * @param  array   $matches The array in which to store matches.
     * @return boolean Success of matching operation.
     */
    public function match($pattern, $source, &$matches)
    {
        return preg_match($this->preparePattern($pattern), $source, $matches);
    }

    /**
     * Matches all patterns in source string and sets the matches array.
     * @param  string  $pattern The regex pattern to match.
     * @param  string  $source  The source string.
     * @param  array   $matches The array in which to store matches.
     * @return boolean Success of matching operation.
     */
    public function matchAll($pattern, $source, &$matches)
    {
        return preg_match_all($this->preparePattern($pattern), $source, $matches);
    }

    /**
     * Replaces $pattern with $replace in $source string.
     * References to \1 group matches will be replaced with more preg-friendly
     * $1.
     * @param  string $pattern The regex pattern to match.
     * @param  string $replace The string with which to replace matches.
     * @param  string $source  The source string.
     * @return string The replaced source string.
     */
    public function replace($pattern, $replace, $source)
    {
        // convert \1 -> $1, because we want to use the more generic \1 in the XML
        // but PREG prefers $1 syntax.
        $replace = preg_replace('/\\\(\d+)/', '\$$1', $replace);

        return preg_replace($this->preparePattern($pattern), $replace, $source, $this->limit);
    }

}
<?php
/*
 *  $Id: ad952b68b1876f57f4854621cfed990abb7fa310 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * A factory class for regex functions.
 * @author Hans Lellelid <hans@xmpl.org>
 * @package  phing.util.regexp
 * @version $Id: ad952b68b1876f57f4854621cfed990abb7fa310 $
 */
class Regexp
{

    /**
     * Matching groups found.
     * @var array
     */
    private $groups = array();

    /**
     * Pattern to match.
     * @var string
     */
    private $pattern;

    /**
     * Replacement pattern.
     * @var string
     */
    private $replace;

    /**
     * The regex engine -- e.g. 'preg' or 'ereg';
     * @var RegexpEngine
     */
    private $engine;

    /**
     * Constructor sets the regex engine to use (preg by default).
     * @param string $engineType
     * @throws BuildException
     */
    public function __construct($engineType = 'preg')
    {
        if ($engineType == 'preg') {
            include_once 'phing/util/regexp/PregEngine.php';
            $this->engine = new PregEngine();
        } else {
            throw new BuildException("Invalid engine type for Regexp: " . $engineType);
        }
    }

    /**
     * Sets pattern to use for matching.
     * @param  string $pat The pattern to match on.
     * @return void
     */
    public function setPattern($pat)
    {
        $this->pattern = (string) $pat;
    }

    /**
     * Gets pattern to use for matching.
     * @return string The pattern to match on.
     */
    public function getPattern()
    {
        return $this->pattern;
    }

    /**
     * Sets replacement string.
     * @param  string $rep The pattern to replace matches with.
     * @return void
     */
    public function setReplace($rep)
    {
        $this->replace = (string) $rep;
    }

    /**
     * Gets replacement string.
     * @return string The pattern to replace matches with.
     */
    public function getReplace()
    {
        return $this->replace;
    }

    /**
     * Performs match of specified pattern against $subject.
     * @param  string $subject The subject, on which to perform matches.
     * @throws Exception
     * @return boolean Whether or not pattern matches subject string passed.
     */
    public function matches($subject)
    {
        if ($this->pattern === null) {
            throw new Exception("No pattern specified for regexp match().");
        }

        return $this->engine->match($this->pattern, $subject, $this->groups);
    }

    /**
     * Performs replacement of specified pattern and replacement strings.
     * @param  string $subject Text on which to perform replacement.
     * @throws Exception
     * @return string subject after replacement has been performed.
     */
    public function replace($subject)
    {
        if ($this->pattern === null || $this->replace === null) {
            throw new Exception("Missing pattern or replacement string regexp replace().");
        }

        return $this->engine->replace($this->pattern, $this->replace, $subject);
    }

    /**
     * Get array of matched groups.
     * @return array Matched groups
     */
    public function getGroups()
    {
        return $this->groups;
    }

    /**
     * Get specific matched group.
     * @param  integer $idx
     * @return string  specified group or NULL if group is not set.
     */
    public function getGroup($idx)
    {
        if (!isset($this->groups[$idx])) {
            return null;
        }

        return $this->groups[$idx];
    }

    /**
     * Sets pattern modifiers for regex engine
     *
     * @param  string $mods Modifiers to be applied to a given regex
     * @return void
     */
    public function setModifiers($mods)
    {
        $this->engine->setModifiers($mods);
    }

    /**
     * Gets pattern modifiers.
     * Subsequent call to engines getModifiers() filters out duplicates
     * i.e. if i is provided in $mods, and setIgnoreCase(true), "i"
     * modifier would be included only once
     * @return string
     */
    public function getModifiers()
    {
        return $this->engine->getModifiers();
    }

    /**
     * Sets whether the regexp matching is case insensitive.
     * (default is false -- i.e. case sensisitive)
     * @param boolean $bit
     */
    public function setIgnoreCase($bit)
    {
        $this->engine->setIgnoreCase($bit);
    }

    /**
     * Gets whether the regexp matching is case insensitive.
     * @return boolean
     */
    public function getIgnoreCase()
    {
        return $this->engine->getIgnoreCase();
    }

    /**
     * Sets whether regexp should be applied in multiline mode.
     * @param boolean $bit
     */
    public function setMultiline($bit)
    {
        $this->engine->setMultiline($bit);
    }

    /**
     * Gets whether regexp is to be applied in multiline mode.
     * @return boolean
     */
    public function getMultiline()
    {
        return $this->engine->getMultiline();
    }

    /**
     * Sets the maximum possible replacements for each pattern.
     * @param int $limit
     */
    public function setLimit($limit)
    {
        $this->engine->setLimit($limit);
    }
}
<?php
/*
 *  $Id: 080377e722e061a58ba3039adbdb9380000031f7 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * Contains some shared attributes and methods -- and some abstract methods with
 * engine-specific implementations that sub-classes must override.
 *
 * @author Hans Lellelid <hans@velum.net>
 * @package phing.util.regexp
 * @version $Id: 080377e722e061a58ba3039adbdb9380000031f7 $
 */
interface RegexpEngine
{

    /**
     * Sets whether or not regex operation should ingore case.
     * @param  boolean $bit
     * @return void
     */
    public function setIgnoreCase($bit);

    /**
     * Returns status of ignore case flag.
     * @return boolean
     */
    public function getIgnoreCase();

    /**
     * Sets whether regexp should be applied in multiline mode.
     * @param boolean $bit
     */
    public function setMultiline($bit);

    /**
     * Gets whether regexp is to be applied in multiline mode.
     * @return boolean
     */
    public function getMultiline();

    /**
     * Sets the maximum possible replacements for each pattern.
     * @param int $limit
     */
    public function setLimit($limit);

    /**
     * Returns the maximum possible replacements for each pattern.
     * @return int
     */
    public function getLimit();

    /**
     * Matches pattern against source string and sets the matches array.
     * @param  string  $pattern The regex pattern to match.
     * @param  string  $source  The source string.
     * @param  array   $matches The array in which to store matches.
     * @return boolean Success of matching operation.
     */
    public function match($pattern, $source, &$matches);

    /**
     * Matches all patterns in source string and sets the matches array.
     * @param  string  $pattern The regex pattern to match.
     * @param  string  $source  The source string.
     * @param  array   $matches The array in which to store matches.
     * @return boolean Success of matching operation.
     */
    public function matchAll($pattern, $source, &$matches);

    /**
     * Replaces $pattern with $replace in $source string.
     * @param  string $pattern The regex pattern to match.
     * @param  string $replace The string with which to replace matches.
     * @param  string $source  The source string.
     * @return string The replaced source string.
     */
    public function replace($pattern, $replace, $source);

}
<?php
/*
 *  $Id: f9dc1f2c2d4fe1ea1e47b2ea1b4fc7652daf8813 $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 *  Utility class that collects the functionality of the various
 *  scanDir methods that have been scattered in several tasks before.
 *
 *  The only method returns an array of source files. The array is a
 *  subset of the files given as a parameter and holds only those that
 *  are newer than their corresponding target files.
 * @package   phing.util
 */
class SourceFileScanner
{

    /** Instance of FileUtils */
    private $fileUtils;

    /** Task this class is working for -- for logging purposes. */
    private $task;

    /**
     * @param Task $task The task we should log messages through
     */
    public function __construct($task)
    {
        $this->task = $task;
        $this->fileUtils = new FileUtils();
    }

    /**
     * Restrict the given set of files to those that are newer than
     * their corresponding target files.
     *
     * @param array $files   the original set of files
     * @param PhingFile $srcDir  all files are relative to this directory
     * @param PhingFile $destDir target files live here. if null file names
     *                returned by the mapper are assumed to be absolute.
     * @param FilenameMapper $mapper  knows how to construct a target file names from
     *                source file names.
     * @param bool $force   Boolean that determines if the files should be
     *                forced to be copied.
     * @return array
     */
    public function restrict(&$files, $srcDir, $destDir, $mapper, $force = false)
    {
        $now = time();
        $targetList = "";

        /*
          If we're on Windows, we have to munge the time up to 2 secs to
          be able to check file modification times.
          (Windows has a max resolution of two secs for modification times)
        */
        $osname = strtolower(Phing::getProperty('os.name'));

        // indexOf()
        $index = ((($res = strpos($osname, 'win')) === false) ? -1 : $res);
        if ($index >= 0) {
            $now += 2000;
        }

        $v = array();

        for ($i = 0, $size = count($files); $i < $size; $i++) {

            $targets = $mapper->main($files[$i]);
            if (empty($targets)) {
                $this->task->log($files[$i] . " skipped - don't know how to handle it", Project::MSG_VERBOSE);
                continue;
            }

            $src = null;
            try {
                if ($srcDir === null) {
                    $src = new PhingFile($files[$i]);
                } else {
                    $src = $this->fileUtils->resolveFile($srcDir, $files[$i]);
                }

                if ($src->lastModified() > $now) {
                    $this->task->log(
                        "Warning: " . $files[$i] . " modified in the future (" . $src->lastModified(
                        ) . " > " . $now . ")",
                        Project::MSG_WARN
                    );
                }
            } catch (IOException $ioe) {
                $this->task->log("Unable to read file " . $files[$i] . " (skipping): " . $ioe->getMessage());
                continue;
            }

            $added = false;
            $targetList = "";

            for ($j = 0, $_j = count($targets); (!$added && $j < $_j); $j++) {

                $dest = null;
                if ($destDir === null) {
                    $dest = new PhingFile($targets[$j]);
                } else {
                    $dest = $this->fileUtils->resolveFile($destDir, $targets[$j]);
                }

                if (!$dest->exists()) {
                    $this->task->log(
                        $files[$i] . " added as " . $dest->__toString() . " doesn't exist.",
                        Project::MSG_VERBOSE
                    );
                    $v[] = $files[$i];
                    $added = true;
                } elseif ($src->lastModified() > $dest->lastModified()) {
                    $this->task->log(
                        $files[$i] . " added as " . $dest->__toString() . " is outdated.",
                        Project::MSG_VERBOSE
                    );
                    $v[] = $files[$i];
                    $added = true;
                } elseif ($force === true) {
                    $this->task->log(
                        $files[$i] . " added as " . $dest->__toString() . " is forced to be overwritten.",
                        Project::MSG_VERBOSE
                    );
                    $v[] = $files[$i];
                    $added = true;
                } else {
                    if (strlen($targetList) > 0) {
                        $targetList .= ", ";
                    }
                    $targetList .= $dest->getAbsolutePath();
                }
            }

            if (!$added) {
                $this->task->log(
                    $files[$i] . " omitted as " . $targetList . " " . (count(
                        $targets
                    ) === 1 ? " is " : " are ") . "up to date.",
                    Project::MSG_VERBOSE
                );
            }

        }
        $result = array();
        $result = $v;

        return $result;
    }

    /**
     * Convenience layer on top of restrict that returns the source
     * files as PhingFile objects (containing absolute paths if srcDir is
     * absolute).
     * @param $files
     * @param $srcDir
     * @param $destDir
     * @param $mapper
     * @return array
     */
    public function restrictAsFiles(&$files, &$srcDir, &$destDir, &$mapper)
    {
        $res = $this->restrict($files, $srcDir, $destDir, $mapper);
        $result = array();
        for ($i = 0; $i < count($res); $i++) {
            $result[$i] = new PhingFile($srcDir, $res[$i]);
        }

        return $result;
    }
}
<?php
/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

/**
 * String helper utility class.
 *
 * This class includes some Java-like functions for parsing strings,
 * as well as some functions for getting qualifiers / unqualifying phing-style
 * classpaths.  (e.g. "phing.util.StringHelper").
 *
 * @author Hans Lellelid <hans@xmpl.org>
 *
 * @package phing.system.util
 */
class StringHelper
{
    /** @var array */
    private static $TRUE_VALUES = array("on", "true", "t", "yes");

    /** @var array */
    private static $FALSE_VALUES = array("off", "false", "f", "no");

    /**
     * Replaces identifier tokens with corresponding text values in passed string.
     *
     * @param  array  $strings      Array of strings to multiply. (If string is passed, will convert to array)
     * @param  array  $tokens       The tokens to search for.
     * @param  array  $replacements The values with which to replace found tokens.
     *
     * @return string
     */
    public static function multiply($strings, $tokens, $replacements)
    {
        $strings = (array) $strings;
        $results = array();
        foreach ($strings as $string) {
            $results[] = str_replace($tokens, $replacements, $string);
        }

        return $results;
    }

    /**
     * Remove qualification to name.
     * E.g. eg.Cat -> Cat
     *
     * @param string $qualifiedName
     * @param string $separator Character used to separate.
     *
     * @return string
     */
    public static function unqualify($qualifiedName, $separator = '.')
    {
        // if false, then will be 0
        $pos = strrpos($qualifiedName, $separator);
        if ($pos === false) {
            return $qualifiedName; // there is no '.' in the qualifed name
        } else {
            return substr($qualifiedName, $pos + 1); // start just after '.'
        }
    }

    /**
     * Converts a string to an indexed array of chars
     * There's really no reason for this to be used in PHP, since strings
     * are all accessible using the $string{0} notation.
     *
     * @param string $str
     *
     * @return array
     *
     * @deprecated
     */
    public static function toCharArray($str)
    {
        $ret = array();
        $len = strlen($str);
        for ($i = 0; $i < $len; $i++) {
            $ret[] = $str{$i};
        }

        return $ret;
    }

    /**
     * Get the qualifier part of a qualified name.
     * E.g. eg.Cat -> eg
     *
     * @param $qualifiedName
     * @param string $separator
     *
     * @return string
     */
    public static function qualifier($qualifiedName, $separator = '.')
    {
        $pos = strrchr($qualifiedName, $separator);
        if ($pos === false) {
            return '';
        } else {
            return substr($qualifiedName, 0, $pos);
        }
    }

    /**
     * @param  array  $columns String[]
     * @param  string $prefix
     *
     * @return array  String[]
     */
    public static function prefix($columns, $prefix)
    {
        if ($prefix == null) {
            return $columns;
        }
        $qualified = array();
        foreach ($columns as $key => $column) {
            $qualified[$key] = $prefix . $column;
        }

        return $qualified;
    }

    /**
     * @param $qualifiedName
     * @param string $separator
     *
     * @return string
     */
    public static function root($qualifiedName, $separator = '.')
    {
        $loc = strpos($qualifiedName, $separator);

        return ($loc === false) ? $qualifiedName : substr($qualifiedName, 0, $loc);
    }

    /**
     * @param $string
     *
     * @return int
     */
    public static function hashCode($string)
    {
        return crc32($string);
    }

    /**
     * @param bool|string $s
     *
     * @return boolean
     */
    public static function booleanValue($s)
    {
        if (is_bool($s)) {
            return $s; // it's already boolean (not a string)
        }
        // otherwise assume it's something like "true" or "t"
        $trimmed = strtolower(trim($s));

        return (boolean) in_array($trimmed, self::$TRUE_VALUES);
    }

    /**
     * tests if a string is a representative of a boolean
     *
     * @param bool|string $s
     *
     * @return bool
     */
    public static function isBoolean($s)
    {

        if (is_bool($s)) {
            return true; // it already is boolean
        }

        if ($s === "" || $s === null || !is_string($s)) {
            return false; // not a valid string for testing
        }

        $test = trim(strtolower($s));

        return (boolean) in_array($test, array_merge(self::$FALSE_VALUES, self::$TRUE_VALUES));
    }

    /**
     * Creates a key based on any number of passed params.
     *
     * @return string
     */
    public static function key()
    {
        $args = func_get_args();

        return serialize($args);
    }

    /**
     * tests if a string starts with a given string
     *
     * @param $check
     * @param $string
     *
     * @return bool
     */
    public static function startsWith($check, $string)
    {
        if ($check === "" || $check === $string) {
            return true;
        } else {
            return (strpos($string, $check) === 0) ? true : false;
        }
    }

    /**
     * tests if a string ends with a given string
     *
     * @param $check
     * @param $string
     *
     * @return bool
     */
    public static function endsWith($check, $string)
    {
        if ($check === "" || $check === $string) {
            return true;
        } else {
            return (strpos(strrev($string), strrev($check)) === 0) ? true : false;
        }
    }

    /**
     * a natural way of getting a subtring, php's circular string buffer and strange
     * return values suck if you want to program strict as of C or friends
     *
     * @param string $string
     * @param int $startpos
     * @param int $endpos
     *
     * @return string
     */
    public static function substring($string, $startpos, $endpos = -1)
    {
        $len = strlen($string);
        $endpos = (int) (($endpos === -1) ? $len - 1 : $endpos);
        if ($startpos > $len - 1 || $startpos < 0) {
            trigger_error("substring(), Startindex out of bounds must be 0<n<$len", E_USER_ERROR);
        }
        if ($endpos > $len - 1 || $endpos < $startpos) {
            trigger_error("substring(), Endindex out of bounds must be $startpos<n<" . ($len - 1), E_USER_ERROR);
        }
        if ($startpos === $endpos) {
            return (string) $string{$startpos};
        } else {
            $len = $endpos - $startpos;
        }

        return substr($string, $startpos, $len + 1);
    }

    /**
     * Does the value correspond to a slot variable?
     *
     * @param string $value
     *
     * @return bool|int
     */
    public static function isSlotVar($value)
    {
        $value = trim($value);
        if ($value === "") {
            return false;
        }

        return preg_match('/^%\{([\w\.\-]+)\}$/', $value);
    }

    /**
     * Extracts the variable name for a slot var in the format %{task.current_file}
     *
     * @param  string $var The var from build file.
     *
     * @return string Extracted name part.
     */
    public static function slotVar($var)
    {
        return trim($var, '%{} ');
    }
}
<?php
/**
 * phpDocumentor
 *
 * PHP Version 5.5
 *
 * @copyright 2010-2015 Mike van Riel / Naenius (http://www.naenius.com)
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection;

/**
 * Interface for Api Elements
 */
interface Element
{
    /**
     * Returns the Fqsen of the element.
     *
     * @return Fqsen
     */
    public function getFqsen();

    /**
     * Returns the name of the element.
     *
     * @return string
     */
    public function getName();
}<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection;

/**
 * Interface for files processed by the ProjectFactory
 */
interface File
{
    /**
     * Returns the content of the file as a string.
     *
     * @return string
     */
    public function getContents();

    /**
     * Returns md5 hash of the file.
     *
     * @return string
     */
    public function md5();

    /**
     * Returns an relative path to the file.
     *
     * @return string
     */
    public function path();
}
<?php
/**
 * phpDocumentor
 *
 * PHP Version 5.5
 *
 * @copyright 2010-2015 Mike van Riel / Naenius (http://www.naenius.com)
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection;

/**
 * Value Object for Fqsen.
 *
 * @link https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc-meta.md
 */
final class Fqsen
{
    /**
     * @var string full quallified class name
     */
    private $fqsen;

    /**
     * @var string name of the element without path.
     */
    private $name;

    /**
     * Initializes the object.
     *
     * @param string $fqsen
     *
     * @throws \InvalidArgumentException when $fqsen is not matching the format.
     */
    public function __construct($fqsen)
    {
        $matches = array();
        $result = preg_match('/^\\\\([\\w_\\\\]*)(?:[:]{2}\\$?([\\w_]+))?(?:\\(\\))?$/', $fqsen, $matches);

        if ($result === 0) {
            throw new \InvalidArgumentException(
                sprintf('"%s" is not a valid Fqsen.', $fqsen)
            );
        }

        $this->fqsen = $fqsen;

        if (isset($matches[2])) {
            $this->name = $matches[2];
        } else {
            $matches = explode('\\', $fqsen);
            $this->name = trim(end($matches), '()');
        }
    }

    /**
     * converts this class to string.
     *
     * @return string
     */
    public function __toString()
    {
        return $this->fqsen;
    }

    /**
     * Returns the name of the element without path.
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection;

/**
 * The location where an element occurs within a file.
 */
final class Location
{
    /** @var int  */
    private $lineNumber = 0;

    /** @var int */
    private $columnNumber = 0;

    /**
     * Initializes the location for an element using its line number in the file and optionally the column number.
     *
     * @param int $lineNumber
     * @param int $columnNumber
     */
    public function __construct($lineNumber, $columnNumber = 0)
    {
        $this->lineNumber   = $lineNumber;
        $this->columnNumber = $columnNumber;
    }

    /**
     * Returns the line number that is covered by this location.
     *
     * @return integer
     */
    public function getLineNumber()
    {
        return $this->lineNumber;
    }

    /**
     * Returns the column number (character position on a line) for this location object.
     *
     * @return integer
     */
    public function getColumnNumber()
    {
        return $this->columnNumber;
    }
}
<?php
/**
 * phpDocumentor
 *
 * PHP Version 5.5
 *
 * @copyright 2010-2015 Mike van Riel / Naenius (http://www.naenius.com)
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection;

/**
 * Interface for project. Since the definition of a project can be different per factory this interface will be small.
 */
interface Project
{
    /**
     * Returns the name of the project.
     *
     * @return string
     */
    public function getName();
}
<?php
/**
 * phpDocumentor
 *
 * PHP Version 5.5
 *
 * @copyright 2010-2015 Mike van Riel / Naenius (http://www.naenius.com)
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */
namespace phpDocumentor\Reflection;

/**
 * Interface for project factories. A project factory shall convert a set of files
 * into an object implementing the Project interface.
 */
interface ProjectFactory
{
    /**
     * Creates a project from the set of files.
     *
     * @param string $name
     * @param File[] $files
     * @return Project
     */
    public function create($name, array $files);
}
<?php
require_once(__DIR__ . '/../vendor/autoload.php');

use phpDocumentor\Reflection\DocBlockFactory;

$docComment = <<<DOCCOMMENT
/**
 * This is an example of a summary.
 *
 * This is a Description. A Summary and Description are separated by either
 * two subsequent newlines (thus a whiteline in between as can be seen in this
 * example), or when the Summary ends with a dot (`.`) and some form of
 * whitespace.
 */
DOCCOMMENT;

$factory  = DocBlockFactory::createInstance();
$docblock = $factory->create($docComment);

// Should contain the first line of the DocBlock
$summary = $docblock->getSummary();

// Contains an object of type Description; you can either cast it to string or use
// the render method to get a string representation of the Description.
//
// In subsequent examples we will be fiddling a bit more with the Description.
$description = $docblock->getDescription();
<?php
require_once(__DIR__ . '/../vendor/autoload.php');

use phpDocumentor\Reflection\DocBlockFactory;

$docComment = <<<DOCCOMMENT
/**
 * This is an example of a summary.
 *
 * @see \phpDocumentor\Reflection\DocBlock\StandardTagFactory
 */
DOCCOMMENT;

$factory  = DocBlockFactory::createInstance();
$docblock = $factory->create($docComment);

// You can check if a DocBlock has one or more see tags
$hasSeeTag = $docblock->hasTag('see');

// Or we can get a complete list of all tags
$tags = $docblock->getTags();

// But we can also grab all tags of a specific type, such as `see`
$seeTags = $docblock->getTagsByName('see');
<?php

require_once(__DIR__ . '/../vendor/autoload.php');

use phpDocumentor\Reflection\DocBlock\Serializer;
use phpDocumentor\Reflection\DocBlockFactory;

$docComment = <<<DOCCOMMENT
/**
 * This is an example of a summary.
 *
 * And here is an example of the description
 * of a DocBlock that can span multiple lines.
 *
 * @see \phpDocumentor\Reflection\DocBlock\StandardTagFactory
 */
DOCCOMMENT;

$factory  = DocBlockFactory::createInstance();
$docblock = $factory->create($docComment);

// Create the serializer that will reconstitute the DocBlock back to its original form.
$serializer = new Serializer();

// Reconstitution is performed by the `getDocComment()` method.
$reconstitutedDocComment = $serializer->getDocComment($docblock);

<?php
/**
 * In this example we demonstrate how you can add your own Tag using a Static Factory method in your Tag class.
 */

require_once(__DIR__ . '/../vendor/autoload.php');

use phpDocumentor\Reflection\DocBlock\Serializer;
use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod;
use phpDocumentor\Reflection\DocBlockFactory;
use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\DocBlock\Tags\BaseTag;
use phpDocumentor\Reflection\Types\Context;
use Webmozart\Assert\Assert;

/**
 * An example of a custom tag called `my-tag` with an optional description.
 *
 * A Custom Tag is a class that can consist of two parts:
 *
 * 1. a method `create` that is a static factory for this class.
 * 2. methods and properties that have this object act as an immutable Value Object representing a Tag instance.
 *
 * The static factory `create` is used to convert a tag line (without the tag name) into an instance of the
 * same tag object with the right constructor parameters set. This method has a dynamic list of parameters so that you
 * can inject various dependencies, see the method's DocBlock for more information.
 *
 * An object of this class, and its methods and properties, represent a single instance of that tag in your
 * documentation in the form of a Value Object whose properties should not be changed after instantiation (it should be
 * immutable).
 *
 * > Important: Tag classes that act as Factories using the `create` method should implement the TagFactory interface.
 */
final class MyTag extends BaseTag implements StaticMethod
{
    /**
     * A required property that is used by Formatters to reconstitute the complete tag line.
     *
     * @see Formatter
     *
     * @var string
     */
    protected $name = 'my-tag';

    /**
     * The constructor for this Tag; this should contain all properties for this object.
     *
     * @param Description $description An example of how to add a Description to the tag; the Description is often
     *                                 an optional variable so passing null is allowed in this instance (though you can
     *                                 also construct an empty description object).
     *
     * @see BaseTag for the declaration of the description property and getDescription method.
     */
    public function __construct(Description $description = null)
    {
        $this->description = $description;
    }

    /**
     * A static Factory that creates a new instance of the current Tag.
     *
     * In this example the MyTag tag can be created by passing a description text as $body. Because we have added
     * a $descriptionFactory that is type-hinted as DescriptionFactory we can now construct a new Description object
     * and pass that to the constructor.
     *
     * > You could directly instantiate a Description object here but that won't be parsed for inline tags and Types
     * > won't be resolved. The DescriptionFactory will take care of those actions.
     *
     * The `create` method's interface states that this method only features a single parameter (`$body`) but the
     * {@see TagFactory} will read the signature of this method and if it has more parameters then it will try
     * to find declarations for it in the ServiceLocator of the TagFactory (see {@see TagFactory::$serviceLocator}).
     *
     * > Important: all properties following the `$body` should default to `null`, otherwise PHP will error because
     * > it no longer matches the interface. This is why you often see the default tags check that an optional argument
     * > is not null nonetheless.
     *
     * @param string             $body
     * @param DescriptionFactory $descriptionFactory
     * @param Context|null       $context The Context is used to resolve Types and FQSENs, although optional
     *                                    it is highly recommended to pass it. If you omit it then it is assumed that
     *                                    the DocBlock is in the global namespace and has no `use` statements.
     *
     * @see Tag for the interface declaration of the `create` method.
     * @see Tag::create() for more information on this method's workings.
     *
     * @return MyTag
     */
    public static function create($body, DescriptionFactory $descriptionFactory = null, Context $context = null)
    {
        Assert::string($body);
        Assert::notNull($descriptionFactory);

        return new static($descriptionFactory->create($body, $context));
    }

    /**
     * Returns a rendition of the original tag line.
     *
     * This method is used to reconstitute a DocBlock into its original form by the {@see Serializer}. It should
     * feature all parts of the tag so that the serializer can put it back together.
     *
     * @return string
     */
    public function __toString()
    {
        return (string)$this->description;
    }
}

$docComment = <<<DOCCOMMENT
/**
 * This is an example of a summary.
 *
 * @my-tag I have a description
 */
DOCCOMMENT;

// Make a mapping between the tag name `my-tag` and the Tag class containing the Factory Method `create`.
$customTags = ['my-tag' => MyTag::class];

// Do pass the list of custom tags to the Factory for the DocBlockFactory.
$factory = DocBlockFactory::createInstance($customTags);
// You can also add Tags later using `$factory->registerTagHandler()` with a tag name and Tag class name.

// Create the DocBlock
$docblock = $factory->create($docComment);

// Take a look: the $customTagObjects now contain an array with your newly added tag
$customTagObjects = $docblock->getTagsByName('my-tag');

// As an experiment: let's reconstitute the DocBlock and observe that because we added a __toString() method
// to the tag class that we can now also see it.
$serializer              = new Serializer();
$reconstitutedDocComment = $serializer->getDocComment($docblock);
<?php

require_once(__DIR__ . '/../../vendor/autoload.php');

use phpDocumentor\Reflection\DocBlockFactory;

$docComment = <<<DOCCOMMENT
/**
 * This is an example of a summary.
 *
 * You can escape the @-sign by surrounding it with braces, for example: {@}. And escape a closing brace within an
 * inline tag by adding an opening brace in front of it like this: {}.
 *
 * Here are example texts where you can see how they could be used in a real life situation:
 *
 *     This is a text with an {@internal inline tag where a closing brace ({}) is shown}.
 *     Or an {@internal inline tag with a literal {{@}link{} in it}.
 *
 * Do note that an {@internal inline tag that has an opening brace ({) does not break out}.
 */
DOCCOMMENT;

$factory  = DocBlockFactory::createInstance();
$docblock = $factory->create($docComment);

// Escaping is automatic so this happens in the DescriptionFactory.
$description = $docblock->getDescription();

// This is the rendition that we will receive of the Description.
$receivedDocComment = <<<DOCCOMMENT
/**
 * This is an example of a summary.
 *
 * You can escape the @-sign by surrounding it with braces, for example: {@}. And escape a closing brace within an
 * inline tag by adding an opening brace in front of it like this: {}.
 *
 * Here are example texts where you can see how they could be used in a real life situation:
 *
 *     This is a text with an {@internal inline tag where a closing brace ({}) is shown}.
 *     Or an {@internal inline tag with a literal {{@}link{} in it}.
 *
 * Do note that an {@internal inline tag that has an opening brace ({) does not break out}.
 */
DOCCOMMENT;

// Render it using the default PassthroughFormatter
$foundDescription = $description->render();
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock;

use phpDocumentor\Reflection\DocBlock\Tags\Formatter;
use phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter;
use Webmozart\Assert\Assert;

/**
 * Object representing to description for a DocBlock.
 *
 * A Description object can consist of plain text but can also include tags. A Description Formatter can then combine
 * a body template with sprintf-style placeholders together with formatted tags in order to reconstitute a complete
 * description text using the format that you would prefer.
 *
 * Because parsing a Description text can be a verbose process this is handled by the {@see DescriptionFactory}. It is
 * thus recommended to use that to create a Description object, like this:
 *
 *     $description = $descriptionFactory->create('This is a {@see Description}', $context);
 *
 * The description factory will interpret the given body and create a body template and list of tags from them, and pass
 * that onto the constructor if this class.
 *
 * > The $context variable is a class of type {@see \phpDocumentor\Reflection\Types\Context} and contains the namespace
 * > and the namespace aliases that apply to this DocBlock. These are used by the Factory to resolve and expand partial
 * > type names and FQSENs.
 *
 * If you do not want to use the DescriptionFactory you can pass a body template and tag listing like this:
 *
 *     $description = new Description(
 *         'This is a %1$s',
 *         [ new See(new Fqsen('\phpDocumentor\Reflection\DocBlock\Description')) ]
 *     );
 *
 * It is generally recommended to use the Factory as that will also apply escaping rules, while the Description object
 * is mainly responsible for rendering.
 *
 * @see DescriptionFactory to create a new Description.
 * @see Description\Formatter for the formatting of the body and tags.
 */
class Description
{
    /** @var string */
    private $bodyTemplate;

    /** @var Tag[] */
    private $tags;

    /**
     * Initializes a Description with its body (template) and a listing of the tags used in the body template.
     *
     * @param string $bodyTemplate
     * @param Tag[] $tags
     */
    public function __construct($bodyTemplate, array $tags = [])
    {
        Assert::string($bodyTemplate);

        $this->bodyTemplate = $bodyTemplate;
        $this->tags = $tags;
    }

    /**
     * Renders this description as a string where the provided formatter will format the tags in the expected string
     * format.
     *
     * @param Formatter|null $formatter
     *
     * @return string
     */
    public function render(Formatter $formatter = null)
    {
        if ($formatter === null) {
            $formatter = new PassthroughFormatter();
        }

        $tags = [];
        foreach ($this->tags as $tag) {
            $tags[] = '{' . $formatter->format($tag) . '}';
        }
        return vsprintf($this->bodyTemplate, $tags);
    }

    /**
     * Returns a plain string representation of this description.
     *
     * @return string
     */
    public function __toString()
    {
        return $this->render();
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock;

use phpDocumentor\Reflection\Types\Context as TypeContext;

/**
 * Creates a new Description object given a body of text.
 *
 * Descriptions in phpDocumentor are somewhat complex entities as they can contain one or more tags inside their
 * body that can be replaced with a readable output. The replacing is done by passing a Formatter object to the
 * Description object's `render` method.
 *
 * In addition to the above does a Description support two types of escape sequences:
 *
 * 1. `{@}` to escape the `@` character to prevent it from being interpreted as part of a tag, i.e. `{{@}link}`
 * 2. `{}` to escape the `}` character, this can be used if you want to use the `}` character in the description
 *    of an inline tag.
 *
 * If a body consists of multiple lines then this factory will also remove any superfluous whitespace at the beginning
 * of each line while maintaining any indentation that is used. This will prevent formatting parsers from tripping
 * over unexpected spaces as can be observed with tag descriptions.
 */
class DescriptionFactory
{
    /** @var TagFactory */
    private $tagFactory;

    /**
     * Initializes this factory with the means to construct (inline) tags.
     *
     * @param TagFactory $tagFactory
     */
    public function __construct(TagFactory $tagFactory)
    {
        $this->tagFactory = $tagFactory;
    }

    /**
     * Returns the parsed text of this description.
     *
     * @param string $contents
     * @param TypeContext $context
     *
     * @return Description
     */
    public function create($contents, TypeContext $context = null)
    {
        list($text, $tags) = $this->parse($this->lex($contents), $context);

        return new Description($text, $tags);
    }

    /**
     * Strips the contents from superfluous whitespace and splits the description into a series of tokens.
     *
     * @param string $contents
     *
     * @return string[] A series of tokens of which the description text is composed.
     */
    private function lex($contents)
    {
        $contents = $this->removeSuperfluousStartingWhitespace($contents);

        // performance optimalization; if there is no inline tag, don't bother splitting it up.
        if (strpos($contents, '{@') === false) {
            return [$contents];
        }

        return preg_split(
            '/\{
                # "{@}" is not a valid inline tag. This ensures that we do not treat it as one, but treat it literally.
                (?!@\})
                # We want to capture the whole tag line, but without the inline tag delimiters.
                (\@
                    # Match everything up to the next delimiter.
                    [^{}]*
                    # Nested inline tag content should not be captured, or it will appear in the result separately.
                    (?:
                        # Match nested inline tags.
                        (?:
                            # Because we did not catch the tag delimiters earlier, we must be explicit with them here.
                            # Notice that this also matches "{}", as a way to later introduce it as an escape sequence.
                            \{(?1)?\}
                            |
                            # Make sure we match hanging "{".
                            \{
                        )
                        # Match content after the nested inline tag.
                        [^{}]*
                    )* # If there are more inline tags, match them as well. We use "*" since there may not be any
                       # nested inline tags.
                )
            \}/Sux',
            $contents,
            null,
            PREG_SPLIT_DELIM_CAPTURE
        );
    }

    /**
     * Parses the stream of tokens in to a new set of tokens containing Tags.
     *
     * @param string[] $tokens
     * @param TypeContext $context
     *
     * @return string[]|Tag[]
     */
    private function parse($tokens, TypeContext $context)
    {
        $count = count($tokens);
        $tagCount = 0;
        $tags  = [];

        for ($i = 1; $i < $count; $i += 2) {
            $tags[] = $this->tagFactory->create($tokens[$i], $context);
            $tokens[$i] = '%' . ++$tagCount . '$s';
        }

        //In order to allow "literal" inline tags, the otherwise invalid
        //sequence "{@}" is changed to "@", and "{}" is changed to "}".
        //"%" is escaped to "%%" because of vsprintf.
        //See unit tests for examples.
        for ($i = 0; $i < $count; $i += 2) {
            $tokens[$i] = str_replace(['{@}', '{}', '%'], ['@', '}', '%%'], $tokens[$i]);
        }

        return [implode('', $tokens), $tags];
    }

    /**
     * Removes the superfluous from a multi-line description.
     *
     * When a description has more than one line then it can happen that the second and subsequent lines have an
     * additional indentation. This is commonly in use with tags like this:
     *
     *     {@}since 1.1.0 This is an example
     *         description where we have an
     *         indentation in the second and
     *         subsequent lines.
     *
     * If we do not normalize the indentation then we have superfluous whitespace on the second and subsequent
     * lines and this may cause rendering issues when, for example, using a Markdown converter.
     *
     * @param string $contents
     *
     * @return string
     */
    private function removeSuperfluousStartingWhitespace($contents)
    {
        $lines = explode("\n", $contents);

        // if there is only one line then we don't have lines with superfluous whitespace and
        // can use the contents as-is
        if (count($lines) <= 1) {
            return $contents;
        }

        // determine how many whitespace characters need to be stripped
        $startingSpaceCount = 9999999;
        for ($i = 1; $i < count($lines); $i++) {
            // lines with a no length do not count as they are not indented at all
            if (strlen(trim($lines[$i])) === 0) {
                continue;
            }

            // determine the number of prefixing spaces by checking the difference in line length before and after
            // an ltrim
            $startingSpaceCount = min($startingSpaceCount, strlen($lines[$i]) - strlen(ltrim($lines[$i])));
        }

        // strip the number of spaces from each line
        if ($startingSpaceCount > 0) {
            for ($i = 1; $i < count($lines); $i++) {
                $lines[$i] = substr($lines[$i], $startingSpaceCount);
            }
        }

        return implode("\n", $lines);
    }

}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection;

use phpDocumentor\Reflection\DocBlock\Tags\Example;

/**
 * Class used to find an example file's location based on a given ExampleDescriptor.
 */
class ExampleFinder
{
    /** @var string */
    private $sourceDirectory = '';

    /** @var string[] */
    private $exampleDirectories = array();

    /**
     * Attempts to find the example contents for the given descriptor.
     *
     * @param Example $example
     *
     * @return string
     */
    public function find(Example $example)
    {
        $filename = $example->getFilePath();

        $file = $this->getExampleFileContents($filename);
        if (!$file) {
            return "** File not found : {$filename} **";
        }

        return implode('', array_slice($file, $example->getStartingLine() - 1, $example->getLineCount()));
    }

    /**
     * Registers the project's root directory where an 'examples' folder can be expected.
     *
     * @param string $directory
     *
     * @return void
     */
    public function setSourceDirectory($directory = '')
    {
        $this->sourceDirectory = $directory;
    }

    /**
     * Returns the project's root directory where an 'examples' folder can be expected.
     *
     * @return string
     */
    public function getSourceDirectory()
    {
        return $this->sourceDirectory;
    }

    /**
     * Registers a series of directories that may contain examples.
     *
     * @param string[] $directories
     */
    public function setExampleDirectories(array $directories)
    {
        $this->exampleDirectories = $directories;
    }

    /**
     * Returns a series of directories that may contain examples.
     *
     * @return string[]
     */
    public function getExampleDirectories()
    {
        return $this->exampleDirectories;
    }

    /**
     * Attempts to find the requested example file and returns its contents or null if no file was found.
     *
     * This method will try several methods in search of the given example file, the first one it encounters is
     * returned:
     *
     * 1. Iterates through all examples folders for the given filename
     * 2. Checks the source folder for the given filename
     * 3. Checks the 'examples' folder in the current working directory for examples
     * 4. Checks the path relative to the current working directory for the given filename
     *
     * @param string $filename
     *
     * @return string|null
     */
    private function getExampleFileContents($filename)
    {
        $normalizedPath = null;

        foreach ($this->exampleDirectories as $directory) {
            $exampleFileFromConfig = $this->constructExamplePath($directory, $filename);
            if (is_readable($exampleFileFromConfig)) {
                $normalizedPath = $exampleFileFromConfig;
                break;
            }
        }

        if (!$normalizedPath) {
            if (is_readable($this->getExamplePathFromSource($filename))) {
                $normalizedPath = $this->getExamplePathFromSource($filename);
            } elseif (is_readable($this->getExamplePathFromExampleDirectory($filename))) {
                $normalizedPath = $this->getExamplePathFromExampleDirectory($filename);
            } elseif (is_readable($filename)) {
                $normalizedPath = $filename;
            }
        }

        return $normalizedPath && is_readable($normalizedPath) ? file($normalizedPath) : null;
    }

    /**
     * Get example filepath based on the example directory inside your project.
     *
     * @param string $file
     *
     * @return string
     */
    private function getExamplePathFromExampleDirectory($file)
    {
        return getcwd() . DIRECTORY_SEPARATOR . 'examples' . DIRECTORY_SEPARATOR . $file;
    }

    /**
     * Returns a path to the example file in the given directory..
     *
     * @param string $directory
     * @param string $file
     *
     * @return string
     */
    private function constructExamplePath($directory, $file)
    {
        return rtrim($directory, '\\/') . DIRECTORY_SEPARATOR . $file;
    }

    /**
     * Get example filepath based on sourcecode.
     *
     * @param string $file
     *
     * @return string
     */
    private function getExamplePathFromSource($file)
    {
        return sprintf(
            '%s%s%s',
            trim($this->getSourceDirectory(), '\\/'),
            DIRECTORY_SEPARATOR,
            trim($file, '"')
        );
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock;

use phpDocumentor\Reflection\DocBlock;
use Webmozart\Assert\Assert;

/**
 * Converts a DocBlock back from an object to a complete DocComment including Asterisks.
 */
class Serializer
{
    /** @var string The string to indent the comment with. */
    protected $indentString = ' ';

    /** @var int The number of times the indent string is repeated. */
    protected $indent = 0;

    /** @var bool Whether to indent the first line with the given indent amount and string. */
    protected $isFirstLineIndented = true;

    /** @var int|null The max length of a line. */
    protected $lineLength = null;

    /**
     * Create a Serializer instance.
     *
     * @param int $indent The number of times the indent string is repeated.
     * @param string   $indentString    The string to indent the comment with.
     * @param bool     $indentFirstLine Whether to indent the first line.
     * @param int|null $lineLength The max length of a line or NULL to disable line wrapping.
     */
    public function __construct($indent = 0, $indentString = ' ', $indentFirstLine = true, $lineLength = null)
    {
        Assert::integer($indent);
        Assert::string($indentString);
        Assert::boolean($indentFirstLine);
        Assert::nullOrInteger($lineLength);

        $this->indent = $indent;
        $this->indentString = $indentString;
        $this->isFirstLineIndented = $indentFirstLine;
        $this->lineLength = $lineLength;
    }

    /**
     * Generate a DocBlock comment.
     *
     * @param DocBlock $docblock The DocBlock to serialize.
     *
     * @return string The serialized doc block.
     */
    public function getDocComment(DocBlock $docblock)
    {
        $indent = str_repeat($this->indentString, $this->indent);
        $firstIndent = $this->isFirstLineIndented ? $indent : '';
        // 3 === strlen(' * ')
        $wrapLength = $this->lineLength ? $this->lineLength - strlen($indent) - 3 : null;

        $text = $this->removeTrailingSpaces(
            $indent,
            $this->addAsterisksForEachLine(
                $indent,
                $this->getSummaryAndDescriptionTextBlock($docblock, $wrapLength)
            )
        );

        $comment = "{$firstIndent}/**\n{$indent} * {$text}\n{$indent} *\n";
        $comment = $this->addTagBlock($docblock, $wrapLength, $indent, $comment);
        $comment .= $indent . ' */';

        return $comment;
    }

    /**
     * @param $indent
     * @param $text
     * @return mixed
     */
    private function removeTrailingSpaces($indent, $text)
    {
        return str_replace("\n{$indent} * \n", "\n{$indent} *\n", $text);
    }

    /**
     * @param $indent
     * @param $text
     * @return mixed
     */
    private function addAsterisksForEachLine($indent, $text)
    {
        return str_replace("\n", "\n{$indent} * ", $text);
    }

    /**
     * @param DocBlock $docblock
     * @param $wrapLength
     * @return string
     */
    private function getSummaryAndDescriptionTextBlock(DocBlock $docblock, $wrapLength)
    {
        $text = $docblock->getSummary() . ((string)$docblock->getDescription() ? "\n\n" . $docblock->getDescription()
                : '');
        if ($wrapLength !== null) {
            $text = wordwrap($text, $wrapLength);
            return $text;
        }
        return $text;
    }

    /**
     * @param DocBlock $docblock
     * @param $wrapLength
     * @param $indent
     * @param $comment
     * @return string
     */
    private function addTagBlock(DocBlock $docblock, $wrapLength, $indent, $comment)
    {
        foreach ($docblock->getTags() as $tag) {
            $formatter = new DocBlock\Tags\Formatter\PassthroughFormatter();
            $tagText   = $formatter->format($tag);
            if ($wrapLength !== null) {
                $tagText = wordwrap($tagText, $wrapLength);
            }
            $tagText = str_replace("\n", "\n{$indent} * ", $tagText);

            $comment .= "{$indent} * {$tagText}\n";
        }

        return $comment;
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock;

use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod;
use phpDocumentor\Reflection\DocBlock\Tags\Generic;
use phpDocumentor\Reflection\FqsenResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;

/**
 * Creates a Tag object given the contents of a tag.
 *
 * This Factory is capable of determining the appropriate class for a tag and instantiate it using its `create`
 * factory method. The `create` factory method of a Tag can have a variable number of arguments; this way you can
 * pass the dependencies that you need to construct a tag object.
 *
 * > Important: each parameter in addition to the body variable for the `create` method must default to null, otherwise
 * > it violates the constraint with the interface; it is recommended to use the {@see Assert::notNull()} method to
 * > verify that a dependency is actually passed.
 *
 * This Factory also features a Service Locator component that is used to pass the right dependencies to the
 * `create` method of a tag; each dependency should be registered as a service or as a parameter.
 *
 * When you want to use a Tag of your own with custom handling you need to call the `registerTagHandler` method, pass
 * the name of the tag and a Fully Qualified Class Name pointing to a class that implements the Tag interface.
 */
final class StandardTagFactory implements TagFactory
{
    /** PCRE regular expression matching a tag name. */
    const REGEX_TAGNAME = '[\w\-\_\\\\]+';

    /**
     * @var string[] An array with a tag as a key, and an FQCN to a class that handles it as an array value.
     */
    private $tagHandlerMappings = [
        'author'         => '\phpDocumentor\Reflection\DocBlock\Tags\Author',
        'covers'         => '\phpDocumentor\Reflection\DocBlock\Tags\Covers',
        'deprecated'     => '\phpDocumentor\Reflection\DocBlock\Tags\Deprecated',
        // 'example'        => '\phpDocumentor\Reflection\DocBlock\Tags\Example',
        'link'           => '\phpDocumentor\Reflection\DocBlock\Tags\Link',
        'method'         => '\phpDocumentor\Reflection\DocBlock\Tags\Method',
        'param'          => '\phpDocumentor\Reflection\DocBlock\Tags\Param',
        'property-read'  => '\phpDocumentor\Reflection\DocBlock\Tags\PropertyRead',
        'property'       => '\phpDocumentor\Reflection\DocBlock\Tags\Property',
        'property-write' => '\phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite',
        'return'         => '\phpDocumentor\Reflection\DocBlock\Tags\Return_',
        'see'            => '\phpDocumentor\Reflection\DocBlock\Tags\See',
        'since'          => '\phpDocumentor\Reflection\DocBlock\Tags\Since',
        'source'         => '\phpDocumentor\Reflection\DocBlock\Tags\Source',
        'throw'          => '\phpDocumentor\Reflection\DocBlock\Tags\Throws',
        'throws'         => '\phpDocumentor\Reflection\DocBlock\Tags\Throws',
        'uses'           => '\phpDocumentor\Reflection\DocBlock\Tags\Uses',
        'var'            => '\phpDocumentor\Reflection\DocBlock\Tags\Var_',
        'version'        => '\phpDocumentor\Reflection\DocBlock\Tags\Version'
    ];

    /**
     * @var \ReflectionParameter[][] a lazy-loading cache containing parameters for each tagHandler that has been used.
     */
    private $tagHandlerParameterCache = [];

    /**
     * @var FqsenResolver
     */
    private $fqsenResolver;

    /**
     * @var mixed[] an array representing a simple Service Locator where we can store parameters and
     *     services that can be inserted into the Factory Methods of Tag Handlers.
     */
    private $serviceLocator = [];

    /**
     * Initialize this tag factory with the means to resolve an FQSEN and optionally a list of tag handlers.
     *
     * If no tag handlers are provided than the default list in the {@see self::$tagHandlerMappings} property
     * is used.
     *
     * @param FqsenResolver $fqsenResolver
     * @param string[]      $tagHandlers
     *
     * @see self::registerTagHandler() to add a new tag handler to the existing default list.
     */
    public function __construct(FqsenResolver $fqsenResolver, array $tagHandlers = null)
    {
        $this->fqsenResolver = $fqsenResolver;
        if ($tagHandlers !== null) {
            $this->tagHandlerMappings = $tagHandlers;
        }

        $this->addService($fqsenResolver, FqsenResolver::class);
    }

    /**
     * {@inheritDoc}
     */
    public function create($tagLine, TypeContext $context = null)
    {
        if (! $context) {
            $context = new TypeContext('');
        }

        list($tagName, $tagBody) = $this->extractTagParts($tagLine);

        return $this->createTag($tagBody, $tagName, $context);
    }

    /**
     * {@inheritDoc}
     */
    public function addParameter($name, $value)
    {
        $this->serviceLocator[$name] = $value;
    }

    /**
     * {@inheritDoc}
     */
    public function addService($service, $alias = null)
    {
        $this->serviceLocator[$alias ?: get_class($service)] = $service;
    }

    /**
     * {@inheritDoc}
     */
    public function registerTagHandler($tagName, $handler)
    {
        Assert::stringNotEmpty($tagName);
        Assert::stringNotEmpty($handler);
        Assert::classExists($handler);
        Assert::implementsInterface($handler, StaticMethod::class);

        if (strpos($tagName, '\\') && $tagName[0] !== '\\') {
            throw new \InvalidArgumentException(
                'A namespaced tag must have a leading backslash as it must be fully qualified'
            );
        }

        $this->tagHandlerMappings[$tagName] = $handler;
    }

    /**
     * Extracts all components for a tag.
     *
     * @param string $tagLine
     *
     * @return string[]
     */
    private function extractTagParts($tagLine)
    {
        $matches = array();
        if (! preg_match('/^@(' . self::REGEX_TAGNAME . ')(?:\s*([^\s].*)|$)?/us', $tagLine, $matches)) {
            throw new \InvalidArgumentException(
                'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors'
            );
        }

        if (count($matches) < 3) {
            $matches[] = '';
        }

        return array_slice($matches, 1);
    }

    /**
     * Creates a new tag object with the given name and body or returns null if the tag name was recognized but the
     * body was invalid.
     *
     * @param string  $body
     * @param string  $name
     * @param TypeContext $context
     *
     * @return Tag|null
     */
    private function createTag($body, $name, TypeContext $context)
    {
        $handlerClassName = $this->findHandlerClassName($name, $context);
        $arguments        = $this->getArgumentsForParametersFromWiring(
            $this->fetchParametersForHandlerFactoryMethod($handlerClassName),
            $this->getServiceLocatorWithDynamicParameters($context, $name, $body)
        )
        ;

        return call_user_func_array([$handlerClassName, 'create'], $arguments);
    }

    /**
     * Determines the Fully Qualified Class Name of the Factory or Tag (containing a Factory Method `create`).
     *
     * @param string  $tagName
     * @param TypeContext $context
     *
     * @return string
     */
    private function findHandlerClassName($tagName, TypeContext $context)
    {
        $handlerClassName = Generic::class;
        if (isset($this->tagHandlerMappings[$tagName])) {
            $handlerClassName = $this->tagHandlerMappings[$tagName];
        } elseif ($this->isAnnotation($tagName)) {
            // TODO: Annotation support is planned for a later stage and as such is disabled for now
            // $tagName = (string)$this->fqsenResolver->resolve($tagName, $context);
            // if (isset($this->annotationMappings[$tagName])) {
            //     $handlerClassName = $this->annotationMappings[$tagName];
            // }
        }

        return $handlerClassName;
    }

    /**
     * Retrieves the arguments that need to be passed to the Factory Method with the given Parameters.
     *
     * @param \ReflectionParameter[] $parameters
     * @param mixed[]                $locator
     *
     * @return mixed[] A series of values that can be passed to the Factory Method of the tag whose parameters
     *     is provided with this method.
     */
    private function getArgumentsForParametersFromWiring($parameters, $locator)
    {
        $arguments = [];
        foreach ($parameters as $index => $parameter) {
            $typeHint = $parameter->getClass() ? $parameter->getClass()->getName() : null;
            if (isset($locator[$typeHint])) {
                $arguments[] = $locator[$typeHint];
                continue;
            }

            $parameterName = $parameter->getName();
            if (isset($locator[$parameterName])) {
                $arguments[] = $locator[$parameterName];
                continue;
            }

            $arguments[] = null;
        }

        return $arguments;
    }

    /**
     * Retrieves a series of ReflectionParameter objects for the static 'create' method of the given
     * tag handler class name.
     *
     * @param string $handlerClassName
     *
     * @return \ReflectionParameter[]
     */
    private function fetchParametersForHandlerFactoryMethod($handlerClassName)
    {
        if (! isset($this->tagHandlerParameterCache[$handlerClassName])) {
            $methodReflection                                  = new \ReflectionMethod($handlerClassName, 'create');
            $this->tagHandlerParameterCache[$handlerClassName] = $methodReflection->getParameters();
        }

        return $this->tagHandlerParameterCache[$handlerClassName];
    }

    /**
     * Returns a copy of this class' Service Locator with added dynamic parameters, such as the tag's name, body and
     * Context.
     *
     * @param TypeContext $context The Context (namespace and aliasses) that may be passed and is used to resolve FQSENs.
     * @param string      $tagName The name of the tag that may be passed onto the factory method of the Tag class.
     * @param string      $tagBody The body of the tag that may be passed onto the factory method of the Tag class.
     *
     * @return mixed[]
     */
    private function getServiceLocatorWithDynamicParameters(TypeContext $context, $tagName, $tagBody)
    {
        $locator = array_merge(
            $this->serviceLocator,
            [
                'name'             => $tagName,
                'body'             => $tagBody,
                TypeContext::class => $context
            ]
        );

        return $locator;
    }

    /**
     * Returns whether the given tag belongs to an annotation.
     *
     * @param string $tagContent
     *
     * @todo this method should be populated once we implement Annotation notation support.
     *
     * @return bool
     */
    private function isAnnotation($tagContent)
    {
        // 1. Contains a namespace separator
        // 2. Contains parenthesis
        // 3. Is present in a list of known annotations (make the algorithm smart by first checking is the last part
        //    of the annotation class name matches the found tag name

        return false;
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock;

use phpDocumentor\Reflection\DocBlock\Tags\Formatter;

interface Tag
{
    public function getName();

    public static function create($body);

    public function render(Formatter $formatter = null);

    public function __toString();
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock;

use phpDocumentor\Reflection\Types\Context as TypeContext;

interface TagFactory
{
    /**
     * Adds a parameter to the service locator that can be injected in a tag's factory method.
     *
     * When calling a tag's "create" method we always check the signature for dependencies to inject. One way is to
     * typehint a parameter in the signature so that we can use that interface or class name to inject a dependency
     * (see {@see addService()} for more information on that).
     *
     * Another way is to check the name of the argument against the names in the Service Locator. With this method
     * you can add a variable that will be inserted when a tag's create method is not typehinted and has a matching
     * name.
     *
     * Be aware that there are two reserved names:
     *
     * - name, representing the name of the tag.
     * - body, representing the complete body of the tag.
     *
     * These parameters are injected at the last moment and will override any existing parameter with those names.
     *
     * @param string $name
     * @param mixed  $value
     *
     * @return void
     */
    public function addParameter($name, $value);

    /**
     * Registers a service with the Service Locator using the FQCN of the class or the alias, if provided.
     *
     * When calling a tag's "create" method we always check the signature for dependencies to inject. If a parameter
     * has a typehint then the ServiceLocator is queried to see if a Service is registered for that typehint.
     *
     * Because interfaces are regularly used as type-hints this method provides an alias parameter; if the FQCN of the
     * interface is passed as alias then every time that interface is requested the provided service will be returned.
     *
     * @param object $service
     * @param string $alias
     *
     * @return void
     */
    public function addService($service);

    /**
     * Factory method responsible for instantiating the correct sub type.
     *
     * @param string $tagLine The text for this tag, including description.
     * @param TypeContext $context
     *
     * @throws \InvalidArgumentException if an invalid tag line was presented.
     *
     * @return Tag A new tag object.
     */
    public function create($tagLine, TypeContext $context = null);

    /**
     * Registers a handler for tags.
     *
     * If you want to use your own tags then you can use this method to instruct the TagFactory to register the name
     * of a tag with the FQCN of a 'Tag Handler'. The Tag handler should implement the {@see Tag} interface (and thus
     * the create method).
     *
     * @param string $tagName Name of tag to register a handler for. When registering a namespaced tag, the full
     *                        name, along with a prefixing slash MUST be provided.
     * @param string $handler FQCN of handler.
     *
     * @throws \InvalidArgumentException if the tag name is not a string
     * @throws \InvalidArgumentException if the tag name is namespaced (contains backslashes) but does not start with
     *     a backslash
     * @throws \InvalidArgumentException if the handler is not a string
     * @throws \InvalidArgumentException if the handler is not an existing class
     * @throws \InvalidArgumentException if the handler does not implement the {@see Tag} interface
     *
     * @return void
     */
    public function registerTagHandler($tagName, $handler);
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use Webmozart\Assert\Assert;

/**
 * Reflection class for an {@}author tag in a Docblock.
 */
final class Author extends BaseTag implements Factory\StaticMethod
{
    /** @var string register that this is the author tag. */
    protected $name = 'author';

    /** @var string The name of the author */
    private $authorName = '';

    /** @var string The email of the author */
    private $authorEmail = '';

    /**
     * Initializes this tag with the author name and e-mail.
     *
     * @param string $authorName
     * @param string $authorEmail
     */
    public function __construct($authorName, $authorEmail)
    {
        Assert::string($authorName);
        Assert::string($authorEmail);
        if ($authorEmail && !filter_var($authorEmail, FILTER_VALIDATE_EMAIL)) {
            throw new \InvalidArgumentException('The author tag does not have a valid e-mail address');
        }

        $this->authorName  = $authorName;
        $this->authorEmail = $authorEmail;
    }

    /**
     * Gets the author's name.
     *
     * @return string The author's name.
     */
    public function getAuthorName()
    {
        return $this->authorName;
    }

    /**
     * Returns the author's email.
     *
     * @return string The author's email.
     */
    public function getEmail()
    {
        return $this->authorEmail;
    }

    /**
     * Returns this tag in string form.
     *
     * @return string
     */
    public function __toString()
    {
        return $this->authorName . '<' . $this->authorEmail . '>';
    }

    /**
     * Attempts to create a new Author object based on †he tag body.
     *
     * @param string $body
     *
     * @return static
     */
    public static function create($body)
    {
        Assert::string($body);

        $splitTagContent = preg_match('/^([^\<]*)(?:\<([^\>]*)\>)?$/u', $body, $matches);
        if (!$splitTagContent) {
            return null;
        }

        $authorName = trim($matches[1]);
        $email = isset($matches[2]) ? trim($matches[2]) : '';

        return new static($authorName, $email);
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\DocBlock;
use phpDocumentor\Reflection\DocBlock\Description;

/**
 * Parses a tag definition for a DocBlock.
 */
abstract class BaseTag implements DocBlock\Tag
{
    /** @var string Name of the tag */
    protected $name = '';

    /** @var Description|null Description of the tag. */
    protected $description;

    /**
     * Gets the name of this tag.
     *
     * @return string The name of this tag.
     */
    public function getName()
    {
        return $this->name;
    }

    public function getDescription()
    {
        return $this->description;
    }

    public function render(Formatter $formatter = null)
    {
        if ($formatter === null) {
            $formatter = new Formatter\PassthroughFormatter();
        }

        return $formatter->format($this);
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Fqsen;
use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\FqsenResolver;
use Webmozart\Assert\Assert;

/**
 * Reflection class for a @covers tag in a Docblock.
 */
final class Covers extends BaseTag implements Factory\StaticMethod
{
    protected $name = 'covers';

    /** @var Fqsen */
    private $refers = null;

    /**
     * Initializes this tag.
     *
     * @param Fqsen $refers
     * @param Description $description
     */
    public function __construct(Fqsen $refers, Description $description = null)
    {
        $this->refers = $refers;
        $this->description = $description;
    }

    /**
     * {@inheritdoc}
     */
    public static function create(
        $body,
        DescriptionFactory $descriptionFactory = null,
        FqsenResolver $resolver = null,
        TypeContext $context = null
    )
    {
        Assert::string($body);
        Assert::notEmpty($body);

        $parts = preg_split('/\s+/Su', $body, 2);

        return new static(
            $resolver->resolve($parts[0], $context),
            $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context)
        );
    }

    /**
     * Returns the structural element this tag refers to.
     *
     * @return Fqsen
     */
    public function getReference()
    {
        return $this->refers;
    }

    /**
     * Returns a string representation of this tag.
     *
     * @return string
     */
    public function __toString()
    {
        return $this->refers . ($this->description ? ' ' . $this->description->render() : '');
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use Webmozart\Assert\Assert;

/**
 * Reflection class for a {@}deprecated tag in a Docblock.
 */
final class Deprecated extends BaseTag implements Factory\StaticMethod
{
    protected $name = 'deprecated';

    /**
     * PCRE regular expression matching a version vector.
     * Assumes the "x" modifier.
     */
    const REGEX_VECTOR = '(?:
        # Normal release vectors.
        \d\S*
        |
        # VCS version vectors. Per PHPCS, they are expected to
        # follow the form of the VCS name, followed by ":", followed
        # by the version vector itself.
        # By convention, popular VCSes like CVS, SVN and GIT use "$"
        # around the actual version vector.
        [^\s\:]+\:\s*\$[^\$]+\$
    )';

    /** @var string The version vector. */
    private $version = '';

    public function __construct($version = null, Description $description = null)
    {
        Assert::nullOrStringNotEmpty($version);

        $this->version = $version;
        $this->description = $description;
    }

    /**
     * @return static
     */
    public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null)
    {
        Assert::nullOrString($body);
        if (empty($body)) {
            return new static();
        }

        $matches = [];
        if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) {
            return new static(
                null,
                null !== $descriptionFactory ? $descriptionFactory->create($body, $context) : null
            );
        }

        return new static(
            $matches[1],
            $descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context)
        );
    }

    /**
     * Gets the version section of the tag.
     *
     * @return string
     */
    public function getVersion()
    {
        return $this->version;
    }

    /**
     * Returns a string representation for this tag.
     *
     * @return string
     */
    public function __toString()
    {
        return $this->version . ($this->description ? ' ' . $this->description->render() : '');
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\DocBlock\Tag;

/**
 * Reflection class for a {@}example tag in a Docblock.
 */
final class Example extends BaseTag
{
    /**
     * @var string Path to a file to use as an example. May also be an absolute URI.
     */
    private $filePath = '';

    /**
     * @var bool Whether the file path component represents an URI. This determines how the file portion
     *     appears at {@link getContent()}.
     */
    private $isURI = false;

    /**
     * {@inheritdoc}
     */
    public function getContent()
    {
        if (null === $this->description) {
            $filePath = '"' . $this->filePath . '"';
            if ($this->isURI) {
                $filePath = $this->isUriRelative($this->filePath)
                    ? str_replace('%2F', '/', rawurlencode($this->filePath))
                    :$this->filePath;
            }

            $this->description = $filePath . ' ' . parent::getContent();
        }

        return $this->description;
    }

    /**
     * {@inheritdoc}
     */
    public static function create($body)
    {
        // File component: File path in quotes or File URI / Source information
        if (! preg_match('/^(?:\"([^\"]+)\"|(\S+))(?:\s+(.*))?$/sux', $body, $matches)) {
            return null;
        }

        $filePath = null;
        $fileUri  = null;
        if ('' !== $matches[1]) {
            $filePath = $matches[1];
        } else {
            $fileUri = $matches[2];
        }

        $startingLine = 1;
        $lineCount    = null;
        $description  = null;

        // Starting line / Number of lines / Description
        if (preg_match('/^([1-9]\d*)\s*(?:((?1))\s+)?(.*)$/sux', $matches[3], $matches)) {
            $startingLine = (int)$matches[1];
            if (isset($matches[2]) && $matches[2] !== '') {
                $lineCount = (int)$matches[2];
            }
            $description = $matches[3];
        }

        return new static($filePath, $fileUri, $startingLine, $lineCount, $description);
    }

    /**
     * Returns the file path.
     *
     * @return string Path to a file to use as an example.
     *     May also be an absolute URI.
     */
    public function getFilePath()
    {
        return $this->filePath;
    }

    /**
     * Sets the file path.
     *
     * @param string $filePath The new file path to use for the example.
     *
     * @return $this
     */
    public function setFilePath($filePath)
    {
        $this->isURI = false;
        $this->filePath = trim($filePath);

        $this->description = null;
        return $this;
    }

    /**
     * Sets the file path as an URI.
     *
     * This function is equivalent to {@link setFilePath()}, except that it
     * converts an URI to a file path before that.
     *
     * There is no getFileURI(), as {@link getFilePath()} is compatible.
     *
     * @param string $uri The new file URI to use as an example.
     *
     * @return $this
     */
    public function setFileURI($uri)
    {
        $this->isURI   = true;
        $this->description = null;

        $this->filePath = $this->isUriRelative($uri)
            ? rawurldecode(str_replace(array('/', '\\'), '%2F', $uri))
            : $this->filePath = $uri;

        return $this;
    }

    /**
     * Returns a string representation for this tag.
     *
     * @return string
     */
    public function __toString()
    {
        return $this->filePath . ($this->description ? ' ' . $this->description->render() : '');
    }

    /**
     * Returns true if the provided URI is relative or contains a complete scheme (and thus is absolute).
     *
     * @param string $uri
     *
     * @return bool
     */
    private function isUriRelative($uri)
    {
        return false === strpos($uri, ':');
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;

interface StaticMethod
{
    public static function create($body);
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;

interface Strategy
{
    public function create($body);
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags\Formatter;

use phpDocumentor\Reflection\DocBlock\Tag;
use phpDocumentor\Reflection\DocBlock\Tags\Formatter;

class PassthroughFormatter implements Formatter
{
    /**
     * Formats the given tag to return a simple plain text version.
     *
     * @param Tag $tag
     *
     * @return string
     */
    public function format(Tag $tag)
    {
        return '@' . $tag->getName() . ' ' . (string)$tag;
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\DocBlock\Tag;

interface Formatter
{
    /**
     * Formats a tag into a string representation according to a specific format, such as Markdown.
     *
     * @param Tag $tag
     *
     * @return string
     */
    public function format(Tag $tag);
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\DocBlock\StandardTagFactory;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;

/**
 * Parses a tag definition for a DocBlock.
 */
class Generic extends BaseTag implements Factory\StaticMethod
{
    /**
     * Parses a tag and populates the member variables.
     *
     * @param string $name Name of the tag.
     * @param Description $description The contents of the given tag.
     */
    public function __construct($name, Description $description = null)
    {
        $this->validateTagName($name);

        $this->name = $name;
        $this->description = $description;
    }

    /**
     * Creates a new tag that represents any unknown tag type.
     *
     * @param string             $body
     * @param string             $name
     * @param DescriptionFactory $descriptionFactory
     * @param TypeContext        $context
     *
     * @return static
     */
    public static function create(
        $body,
        $name = '',
        DescriptionFactory $descriptionFactory = null,
        TypeContext $context = null
    ) {
        Assert::string($body);
        Assert::stringNotEmpty($name);
        Assert::notNull($descriptionFactory);

        $description = $descriptionFactory && $body ? $descriptionFactory->create($body, $context) : null;

        return new static($name, $description);
    }

    /**
     * Returns the tag as a serialized string
     *
     * @return string
     */
    public function __toString()
    {
        return ($this->description ? $this->description->render() : '');
    }

    /**
     * Validates if the tag name matches the expected format, otherwise throws an exception.
     *
     * @param string $name
     *
     * @return void
     */
    private function validateTagName($name)
    {
        if (! preg_match('/^' . StandardTagFactory::REGEX_TAGNAME . '$/u', $name)) {
            throw new \InvalidArgumentException(
                'The tag name "' . $name . '" is not wellformed. Tags may only consist of letters, underscores, '
                . 'hyphens and backslashes.'
            );
        }
    }
}
<?php
/**
 * phpDocumentor
 *
 * PHP Version 5.3
 *
 * @author    Ben Selby <benmatselby@gmail.com>
 * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;

/**
 * Reflection class for a @link tag in a Docblock.
 */
final class Link extends BaseTag implements Factory\StaticMethod
{
    protected $name = 'link';

    /** @var string */
    private $link = '';

    /**
     * Initializes a link to a URL.
     *
     * @param string      $link
     * @param Description $description
     */
    public function __construct($link, Description $description = null)
    {
        Assert::string($link);

        $this->link = $link;
        $this->description = $description;
    }

    /**
     * {@inheritdoc}
     */
    public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null)
    {
        Assert::string($body);
        Assert::notNull($descriptionFactory);

        $parts = preg_split('/\s+/Su', $body, 2);
        $description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null;

        return new static($parts[0], $description);
    }

    /**
    * Gets the link
    *
    * @return string
    */
    public function getLink()
    {
        return $this->link;
    }

    /**
     * Returns a string representation for this tag.
     *
     * @return string
     */
    public function __toString()
    {
        return $this->link . ($this->description ? ' ' . $this->description->render() : '');
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\Types\Void_;
use Webmozart\Assert\Assert;

/**
 * Reflection class for an {@}method in a Docblock.
 */
final class Method extends BaseTag implements Factory\StaticMethod
{
    protected $name = 'method';

    /** @var string */
    private $methodName = '';

    /** @var string[] */
    private $arguments = [];

    /** @var bool */
    private $isStatic = false;

    /** @var Type */
    private $returnType;

    public function __construct(
        $methodName,
        array $arguments = [],
        Type $returnType = null,
        $static = false,
        Description $description = null
    ) {
        Assert::stringNotEmpty($methodName);
        Assert::boolean($static);

        if ($returnType === null) {
            $returnType = new Void_();
        }

        $this->methodName  = $methodName;
        $this->arguments   = $this->filterArguments($arguments);
        $this->returnType  = $returnType;
        $this->isStatic    = $static;
        $this->description = $description;
    }

    /**
     * {@inheritdoc}
     */
    public static function create(
        $body,
        TypeResolver $typeResolver = null,
        DescriptionFactory $descriptionFactory = null,
        TypeContext $context = null
    ) {
        Assert::stringNotEmpty($body);
        Assert::allNotNull([ $typeResolver, $descriptionFactory ]);

        // 1. none or more whitespace
        // 2. optionally the keyword "static" followed by whitespace
        // 3. optionally a word with underscores followed by whitespace : as
        //    type for the return value
        // 4. then optionally a word with underscores followed by () and
        //    whitespace : as method name as used by phpDocumentor
        // 5. then a word with underscores, followed by ( and any character
        //    until a ) and whitespace : as method name with signature
        // 6. any remaining text : as description
        if (!preg_match(
            '/^
                # Static keyword
                # Declares a static method ONLY if type is also present
                (?:
                    (static)
                    \s+
                )?
                # Return type
                (?:
                    (
                        (?:[\w\|_\\\\]+)
                        # array notation           
                        (?:\[\])*
                    )?
                    \s+
                )?
                # Legacy method name (not captured)
                (?:
                    [\w_]+\(\)\s+
                )?
                # Method name
                ([\w\|_\\\\]+)
                # Arguments
                (?:
                    \(([^\)]*)\)
                )?
                \s*
                # Description
                (.*)
            $/sux',
            $body,
            $matches
        )) {
            return null;
        }

        list(, $static, $returnType, $methodName, $arguments, $description) = $matches;

        $static      = $static === 'static';
        $returnType  = $typeResolver->resolve($returnType, $context);
        $description = $descriptionFactory->create($description, $context);

        if ('' !== $arguments) {
            $arguments = explode(',', $arguments);
            foreach($arguments as &$argument) {
                $argument = explode(' ', self::stripRestArg(trim($argument)), 2);
                if ($argument[0][0] === '$') {
                    $argumentName = substr($argument[0], 1);
                    $argumentType = new Void_();
                } else {
                    $argumentType = $typeResolver->resolve($argument[0], $context);
                    $argumentName = '';
                    if (isset($argument[1])) {
                        $argument[1] = self::stripRestArg($argument[1]);
                        $argumentName = substr($argument[1], 1);
                    }
                }

                $argument = [ 'name' => $argumentName, 'type' => $argumentType];
            }
        } else {
            $arguments = [];
        }

        return new static($methodName, $arguments, $returnType, $static, $description);
    }

    /**
     * Retrieves the method name.
     *
     * @return string
     */
    public function getMethodName()
    {
        return $this->methodName;
    }

    /**
     * @return string[]
     */
    public function getArguments()
    {
        return $this->arguments;
    }

    /**
     * Checks whether the method tag describes a static method or not.
     *
     * @return bool TRUE if the method declaration is for a static method, FALSE otherwise.
     */
    public function isStatic()
    {
        return $this->isStatic;
    }

    /**
     * @return Type
     */
    public function getReturnType()
    {
        return $this->returnType;
    }

    public function __toString()
    {
        $arguments = [];
        foreach ($this->arguments as $argument) {
            $arguments[] = $argument['type'] . ' $' . $argument['name'];
        }

        return ($this->isStatic() ? 'static ' : '')
            . (string)$this->returnType . ' '
            . $this->methodName
            . '(' . implode(', ', $arguments) . ')'
            . ($this->description ? ' ' . $this->description->render() : '');
    }

    private function filterArguments($arguments)
    {
        foreach ($arguments as &$argument) {
            if (is_string($argument)) {
                $argument = [ 'name' => $argument ];
            }
            if (! isset($argument['type'])) {
                $argument['type'] = new Void_();
            }
            $keys = array_keys($argument);
            if ($keys !== [ 'name', 'type' ]) {
                throw new \InvalidArgumentException(
                    'Arguments can only have the "name" and "type" fields, found: ' . var_export($keys, true)
                );
            }
        }

        return $arguments;
    }

    private static function stripRestArg($argument)
    {
        if (strpos($argument, '...') === 0) {
            $argument = trim(substr($argument, 3));
        }

        return $argument;
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;

/**
 * Reflection class for the {@}param tag in a Docblock.
 */
final class Param extends BaseTag implements Factory\StaticMethod
{
    /** @var string */
    protected $name = 'param';

    /** @var Type */
    private $type;

    /** @var string */
    private $variableName = '';

    /** @var bool determines whether this is a variadic argument */
    private $isVariadic = false;

    /**
     * @param string $variableName
     * @param Type $type
     * @param bool $isVariadic
     * @param Description $description
     */
    public function __construct($variableName, Type $type = null, $isVariadic = false, Description $description = null)
    {
        Assert::string($variableName);
        Assert::boolean($isVariadic);

        $this->variableName = $variableName;
        $this->type = $type;
        $this->isVariadic = $isVariadic;
        $this->description = $description;
    }

    /**
     * {@inheritdoc}
     */
    public static function create(
        $body,
        TypeResolver $typeResolver = null,
        DescriptionFactory $descriptionFactory = null,
        TypeContext $context = null
    ) {
        Assert::stringNotEmpty($body);
        Assert::allNotNull([$typeResolver, $descriptionFactory]);

        $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE);
        $type = null;
        $variableName = '';
        $isVariadic = false;

        // if the first item that is encountered is not a variable; it is a type
        if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) {
            $type = $typeResolver->resolve(array_shift($parts), $context);
            array_shift($parts);
        }

        // if the next item starts with a $ or ...$ it must be the variable name
        if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] == '$' || substr($parts[0], 0, 4) === '...$')) {
            $variableName = array_shift($parts);
            array_shift($parts);

            if (substr($variableName, 0, 3) === '...') {
                $isVariadic = true;
                $variableName = substr($variableName, 3);
            }

            if (substr($variableName, 0, 1) === '$') {
                $variableName = substr($variableName, 1);
            }
        }

        $description = $descriptionFactory->create(implode('', $parts), $context);

        return new static($variableName, $type, $isVariadic, $description);
    }

    /**
     * Returns the variable's name.
     *
     * @return string
     */
    public function getVariableName()
    {
        return $this->variableName;
    }

    /**
     * Returns the variable's type or null if unknown.
     *
     * @return Type|null
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Returns whether this tag is variadic.
     *
     * @return boolean
     */
    public function isVariadic()
    {
        return $this->isVariadic;
    }

    /**
     * Returns a string representation for this tag.
     *
     * @return string
     */
    public function __toString()
    {
        return ($this->type ? $this->type . ' ' : '')
        . ($this->isVariadic() ? '...' : '')
        . '$' . $this->variableName
        . ($this->description ? ' ' . $this->description : '');
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;

/**
 * Reflection class for a {@}property tag in a Docblock.
 */
class Property extends BaseTag implements Factory\StaticMethod
{
    /** @var string */
    protected $name = 'property';

    /** @var Type */
    private $type;

    /** @var string */
    protected $variableName = '';

    /**
     * @param string      $variableName
     * @param Type        $type
     * @param Description $description
     */
    public function __construct($variableName, Type $type = null, Description $description = null)
    {
        Assert::string($variableName);

        $this->variableName = $variableName;
        $this->type = $type;
        $this->description = $description;
    }

    /**
     * {@inheritdoc}
     */
    public static function create(
        $body,
        TypeResolver $typeResolver = null,
        DescriptionFactory $descriptionFactory = null,
        TypeContext $context = null
    ) {
        Assert::stringNotEmpty($body);
        Assert::allNotNull([$typeResolver, $descriptionFactory]);

        $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE);
        $type = null;
        $variableName = '';

        // if the first item that is encountered is not a variable; it is a type
        if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) {
            $type = $typeResolver->resolve(array_shift($parts), $context);
            array_shift($parts);
        }

        // if the next item starts with a $ or ...$ it must be the variable name
        if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] == '$')) {
            $variableName = array_shift($parts);
            array_shift($parts);

            if (substr($variableName, 0, 1) === '$') {
                $variableName = substr($variableName, 1);
            }
        }

        $description = $descriptionFactory->create(implode('', $parts), $context);

        return new static($variableName, $type, $description);
    }

    /**
     * Returns the variable's name.
     *
     * @return string
     */
    public function getVariableName()
    {
        return $this->variableName;
    }

    /**
     * Returns the variable's type or null if unknown.
     *
     * @return Type|null
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Returns a string representation for this tag.
     *
     * @return string
     */
    public function __toString()
    {
        return ($this->type ? $this->type . ' ' : '')
        . '$' . $this->variableName
        . ($this->description ? ' ' . $this->description : '');
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;

/**
 * Reflection class for a {@}property-read tag in a Docblock.
 */
class PropertyRead extends BaseTag implements Factory\StaticMethod
{
    /** @var string */
    protected $name = 'property-read';

    /** @var Type */
    private $type;

    /** @var string */
    protected $variableName = '';

    /**
     * @param string      $variableName
     * @param Type        $type
     * @param Description $description
     */
    public function __construct($variableName, Type $type = null, Description $description = null)
    {
        Assert::string($variableName);

        $this->variableName = $variableName;
        $this->type = $type;
        $this->description = $description;
    }

    /**
     * {@inheritdoc}
     */
    public static function create(
        $body,
        TypeResolver $typeResolver = null,
        DescriptionFactory $descriptionFactory = null,
        TypeContext $context = null
    ) {
        Assert::stringNotEmpty($body);
        Assert::allNotNull([$typeResolver, $descriptionFactory]);

        $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE);
        $type = null;
        $variableName = '';

        // if the first item that is encountered is not a variable; it is a type
        if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) {
            $type = $typeResolver->resolve(array_shift($parts), $context);
            array_shift($parts);
        }

        // if the next item starts with a $ or ...$ it must be the variable name
        if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] == '$')) {
            $variableName = array_shift($parts);
            array_shift($parts);

            if (substr($variableName, 0, 1) === '$') {
                $variableName = substr($variableName, 1);
            }
        }

        $description = $descriptionFactory->create(implode('', $parts), $context);

        return new static($variableName, $type, $description);
    }

    /**
     * Returns the variable's name.
     *
     * @return string
     */
    public function getVariableName()
    {
        return $this->variableName;
    }

    /**
     * Returns the variable's type or null if unknown.
     *
     * @return Type|null
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Returns a string representation for this tag.
     *
     * @return string
     */
    public function __toString()
    {
        return ($this->type ? $this->type . ' ' : '')
        . '$' . $this->variableName
        . ($this->description ? ' ' . $this->description : '');
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;

/**
 * Reflection class for a {@}property-write tag in a Docblock.
 */
class PropertyWrite extends BaseTag implements Factory\StaticMethod
{
    /** @var string */
    protected $name = 'property-write';

    /** @var Type */
    private $type;

    /** @var string */
    protected $variableName = '';

    /**
     * @param string      $variableName
     * @param Type        $type
     * @param Description $description
     */
    public function __construct($variableName, Type $type = null, Description $description = null)
    {
        Assert::string($variableName);

        $this->variableName = $variableName;
        $this->type = $type;
        $this->description = $description;
    }

    /**
     * {@inheritdoc}
     */
    public static function create(
        $body,
        TypeResolver $typeResolver = null,
        DescriptionFactory $descriptionFactory = null,
        TypeContext $context = null
    ) {
        Assert::stringNotEmpty($body);
        Assert::allNotNull([$typeResolver, $descriptionFactory]);

        $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE);
        $type = null;
        $variableName = '';

        // if the first item that is encountered is not a variable; it is a type
        if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) {
            $type = $typeResolver->resolve(array_shift($parts), $context);
            array_shift($parts);
        }

        // if the next item starts with a $ or ...$ it must be the variable name
        if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] == '$')) {
            $variableName = array_shift($parts);
            array_shift($parts);

            if (substr($variableName, 0, 1) === '$') {
                $variableName = substr($variableName, 1);
            }
        }

        $description = $descriptionFactory->create(implode('', $parts), $context);

        return new static($variableName, $type, $description);
    }

    /**
     * Returns the variable's name.
     *
     * @return string
     */
    public function getVariableName()
    {
        return $this->variableName;
    }

    /**
     * Returns the variable's type or null if unknown.
     *
     * @return Type|null
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Returns a string representation for this tag.
     *
     * @return string
     */
    public function __toString()
    {
        return ($this->type ? $this->type . ' ' : '')
        . '$' . $this->variableName
        . ($this->description ? ' ' . $this->description : '');
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;

/**
 * Reflection class for a {@}return tag in a Docblock.
 */
final class Return_ extends BaseTag implements Factory\StaticMethod
{
    protected $name = 'return';

    /** @var Type */
    private $type;

    public function __construct(Type $type, Description $description = null)
    {
        $this->type = $type;
        $this->description = $description;
    }

    /**
     * {@inheritdoc}
     */
    public static function create(
        $body,
        TypeResolver $typeResolver = null,
        DescriptionFactory $descriptionFactory = null,
        TypeContext $context = null
    )
    {
        Assert::string($body);
        Assert::allNotNull([$typeResolver, $descriptionFactory]);

        $parts = preg_split('/\s+/Su', $body, 2);

        $type = $typeResolver->resolve(isset($parts[0]) ? $parts[0] : '', $context);
        $description = $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context);

        return new static($type, $description);
    }

    /**
     * Returns the type section of the variable.
     *
     * @return Type
     */
    public function getType()
    {
        return $this->type;
    }

    public function __toString()
    {
        return $this->type . ' ' . $this->description;
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Fqsen;
use phpDocumentor\Reflection\FqsenResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\DocBlock\Description;
use Webmozart\Assert\Assert;

/**
 * Reflection class for an {@}see tag in a Docblock.
 */
class See extends BaseTag implements Factory\StaticMethod
{
    protected $name = 'see';

    /** @var Fqsen */
    protected $refers = null;

    /**
     * Initializes this tag.
     *
     * @param Fqsen $refers
     * @param Description $description
     */
    public function __construct(Fqsen $refers, Description $description = null)
    {
        $this->refers = $refers;
        $this->description = $description;
    }

    /**
     * {@inheritdoc}
     */
    public static function create(
        $body,
        FqsenResolver $resolver = null,
        DescriptionFactory $descriptionFactory = null,
        TypeContext $context = null
    ) {
        Assert::string($body);
        Assert::allNotNull([$resolver, $descriptionFactory]);

        $parts       = preg_split('/\s+/Su', $body, 2);
        $description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null;

        return new static($resolver->resolve($parts[0], $context), $description);
    }

    /**
     * Returns the structural element this tag refers to.
     *
     * @return Fqsen
     */
    public function getReference()
    {
        return $this->refers;
    }

    /**
     * Returns a string representation of this tag.
     *
     * @return string
     */
    public function __toString()
    {
        return $this->refers . ($this->description ? ' ' . $this->description->render() : '');
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use Webmozart\Assert\Assert;

/**
 * Reflection class for a {@}since tag in a Docblock.
 */
final class Since extends BaseTag implements Factory\StaticMethod
{
    protected $name = 'since';

    /**
     * PCRE regular expression matching a version vector.
     * Assumes the "x" modifier.
     */
    const REGEX_VECTOR = '(?:
        # Normal release vectors.
        \d\S*
        |
        # VCS version vectors. Per PHPCS, they are expected to
        # follow the form of the VCS name, followed by ":", followed
        # by the version vector itself.
        # By convention, popular VCSes like CVS, SVN and GIT use "$"
        # around the actual version vector.
        [^\s\:]+\:\s*\$[^\$]+\$
    )';

    /** @var string The version vector. */
    private $version = '';

    public function __construct($version = null, Description $description = null)
    {
        Assert::nullOrStringNotEmpty($version);

        $this->version     = $version;
        $this->description = $description;
    }

    /**
     * @return static
     */
    public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null)
    {
        Assert::nullOrString($body);
        if (empty($body)) {
            return new static();
        }

        $matches = [];
        if (! preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) {
            return null;
        }

        return new static(
            $matches[1],
            $descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context)
        );
    }

    /**
     * Gets the version section of the tag.
     *
     * @return string
     */
    public function getVersion()
    {
        return $this->version;
    }

    /**
     * Returns a string representation for this tag.
     *
     * @return string
     */
    public function __toString()
    {
        return $this->version . ($this->description ? ' ' . $this->description->render() : '');
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;

/**
 * Reflection class for a {@}source tag in a Docblock.
 */
final class Source extends BaseTag implements Factory\StaticMethod
{
    /** @var string */
    protected $name = 'source';

    /** @var int The starting line, relative to the structural element's location. */
    private $startingLine = 1;

    /** @var int|null The number of lines, relative to the starting line. NULL means "to the end". */
    private $lineCount = null;

    public function __construct($startingLine, $lineCount = null, Description $description = null)
    {
        Assert::integerish($startingLine);
        Assert::nullOrIntegerish($lineCount);

        $this->startingLine = (int)$startingLine;
        $this->lineCount    = $lineCount !== null ? (int)$lineCount : null;
        $this->description  = $description;
    }

    /**
     * {@inheritdoc}
     */
    public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null)
    {
        Assert::stringNotEmpty($body);
        Assert::notNull($descriptionFactory);

        $startingLine = 1;
        $lineCount    = null;
        $description  = null;

        // Starting line / Number of lines / Description
        if (preg_match('/^([1-9]\d*)\s*(?:((?1))\s+)?(.*)$/sux', $body, $matches)) {
            $startingLine = (int)$matches[1];
            if (isset($matches[2]) && $matches[2] !== '') {
                $lineCount = (int)$matches[2];
            }
            $description = $matches[3];
        }

        return new static($startingLine, $lineCount, $descriptionFactory->create($description, $context));
    }

    /**
     * Gets the starting line.
     *
     * @return int The starting line, relative to the structural element's
     *     location.
     */
    public function getStartingLine()
    {
        return $this->startingLine;
    }

    /**
     * Returns the number of lines.
     *
     * @return int|null The number of lines, relative to the starting line. NULL
     *     means "to the end".
     */
    public function getLineCount()
    {
        return $this->lineCount;
    }

    public function __toString()
    {
        return $this->startingLine
        . ($this->lineCount !== null ? ' ' . $this->lineCount : '')
        . ($this->description ? ' ' . $this->description->render() : '');
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;

/**
 * Reflection class for a {@}throws tag in a Docblock.
 */
final class Throws extends BaseTag implements Factory\StaticMethod
{
    protected $name = 'throws';

    /** @var Type */
    private $type;

    public function __construct(Type $type, Description $description = null)
    {
        $this->type        = $type;
        $this->description = $description;
    }

    /**
     * {@inheritdoc}
     */
    public static function create(
        $body,
        TypeResolver $typeResolver = null,
        DescriptionFactory $descriptionFactory = null,
        TypeContext $context = null
    ) {
        Assert::string($body);
        Assert::allNotNull([$typeResolver, $descriptionFactory]);

        $parts = preg_split('/\s+/Su', $body, 2);

        $type        = $typeResolver->resolve(isset($parts[0]) ? $parts[0] : '', $context);
        $description = $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context);

        return new static($type, $description);
    }

    /**
     * Returns the type section of the variable.
     *
     * @return Type
     */
    public function getType()
    {
        return $this->type;
    }

    public function __toString()
    {
        return $this->type . ' ' . $this->description;
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Fqsen;
use phpDocumentor\Reflection\FqsenResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;

/**
 * Reflection class for a {@}uses tag in a Docblock.
 */
final class Uses extends BaseTag implements Factory\StaticMethod
{
    protected $name = 'uses';

    /** @var Fqsen */
    protected $refers = null;

    /**
     * Initializes this tag.
     *
     * @param Fqsen       $refers
     * @param Description $description
     */
    public function __construct(Fqsen $refers, Description $description = null)
    {
        $this->refers      = $refers;
        $this->description = $description;
    }

    /**
     * {@inheritdoc}
     */
    public static function create(
        $body,
        FqsenResolver $resolver = null,
        DescriptionFactory $descriptionFactory = null,
        TypeContext $context = null
    ) {
        Assert::string($body);
        Assert::allNotNull([$resolver, $descriptionFactory]);

        $parts = preg_split('/\s+/Su', $body, 2);

        return new static(
            $resolver->resolve($parts[0], $context),
            $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context)
        );
    }

    /**
     * Returns the structural element this tag refers to.
     *
     * @return Fqsen
     */
    public function getReference()
    {
        return $this->refers;
    }

    /**
     * Returns a string representation of this tag.
     *
     * @return string
     */
    public function __toString()
    {
        return $this->refers . ' ' . $this->description->render();
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;

/**
 * Reflection class for a {@}var tag in a Docblock.
 */
class Var_ extends BaseTag implements Factory\StaticMethod
{
    /** @var string */
    protected $name = 'var';

    /** @var Type */
    private $type;

    /** @var string */
    protected $variableName = '';

    /**
     * @param string      $variableName
     * @param Type        $type
     * @param Description $description
     */
    public function __construct($variableName, Type $type = null, Description $description = null)
    {
        Assert::string($variableName);

        $this->variableName = $variableName;
        $this->type         = $type;
        $this->description  = $description;
    }

    /**
     * {@inheritdoc}
     */
    public static function create(
        $body,
        TypeResolver $typeResolver = null,
        DescriptionFactory $descriptionFactory = null,
        TypeContext $context = null
    ) {
        Assert::stringNotEmpty($body);
        Assert::allNotNull([$typeResolver, $descriptionFactory]);

        $parts        = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE);
        $type         = null;
        $variableName = '';

        // if the first item that is encountered is not a variable; it is a type
        if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) {
            $type = $typeResolver->resolve(array_shift($parts), $context);
            array_shift($parts);
        }

        // if the next item starts with a $ or ...$ it must be the variable name
        if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] == '$')) {
            $variableName = array_shift($parts);
            array_shift($parts);

            if (substr($variableName, 0, 1) === '$') {
                $variableName = substr($variableName, 1);
            }
        }

        $description = $descriptionFactory->create(implode('', $parts), $context);

        return new static($variableName, $type, $description);
    }

    /**
     * Returns the variable's name.
     *
     * @return string
     */
    public function getVariableName()
    {
        return $this->variableName;
    }

    /**
     * Returns the variable's type or null if unknown.
     *
     * @return Type|null
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Returns a string representation for this tag.
     *
     * @return string
     */
    public function __toString()
    {
        return ($this->type ? $this->type . ' ' : '')
        . '$' . $this->variableName
        . ($this->description ? ' ' . $this->description : '');
    }
}
<?php
/**
 * phpDocumentor
 *
 * PHP Version 5.3
 *
 * @author    Vasil Rangelov <boen.robot@gmail.com>
 * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\DocBlock\Tags;

use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use Webmozart\Assert\Assert;

/**
 * Reflection class for a {@}version tag in a Docblock.
 */
final class Version extends BaseTag implements Factory\StaticMethod
{
    protected $name = 'version';

    /**
     * PCRE regular expression matching a version vector.
     * Assumes the "x" modifier.
     */
    const REGEX_VECTOR = '(?:
        # Normal release vectors.
        \d\S*
        |
        # VCS version vectors. Per PHPCS, they are expected to
        # follow the form of the VCS name, followed by ":", followed
        # by the version vector itself.
        # By convention, popular VCSes like CVS, SVN and GIT use "$"
        # around the actual version vector.
        [^\s\:]+\:\s*\$[^\$]+\$
    )';

    /** @var string The version vector. */
    private $version = '';

    public function __construct($version = null, Description $description = null)
    {
        Assert::nullOrStringNotEmpty($version);

        $this->version = $version;
        $this->description = $description;
    }

    /**
     * @return static
     */
    public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null)
    {
        Assert::nullOrString($body);
        if (empty($body)) {
            return new static();
        }

        $matches = [];
        if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) {
            return null;
        }

        return new static(
            $matches[1],
            $descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context)
        );
    }

    /**
     * Gets the version section of the tag.
     *
     * @return string
     */
    public function getVersion()
    {
        return $this->version;
    }

    /**
     * Returns a string representation for this tag.
     *
     * @return string
     */
    public function __toString()
    {
        return $this->version . ($this->description ? ' ' . $this->description->render() : '');
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection;

use phpDocumentor\Reflection\DocBlock\Tag;
use Webmozart\Assert\Assert;

final class DocBlock
{
    /** @var string The opening line for this docblock. */
    private $summary = '';

    /** @var DocBlock\Description The actual description for this docblock. */
    private $description = null;

    /** @var Tag[] An array containing all the tags in this docblock; except inline. */
    private $tags = array();

    /** @var Types\Context Information about the context of this DocBlock. */
    private $context = null;

    /** @var Location Information about the location of this DocBlock. */
    private $location = null;

    /** @var bool Is this DocBlock (the start of) a template? */
    private $isTemplateStart = false;

    /** @var bool Does this DocBlock signify the end of a DocBlock template? */
    private $isTemplateEnd = false;

    /**
     * @param string $summary
     * @param DocBlock\Description $description
     * @param DocBlock\Tag[] $tags
     * @param Types\Context $context The context in which the DocBlock occurs.
     * @param Location $location The location within the file that this DocBlock occurs in.
     * @param bool $isTemplateStart
     * @param bool $isTemplateEnd
     */
    public function __construct(
        $summary = '',
        DocBlock\Description $description = null,
        array $tags = [],
        Types\Context $context = null,
        Location $location = null,
        $isTemplateStart = false,
        $isTemplateEnd = false
    )
    {
        Assert::string($summary);
        Assert::boolean($isTemplateStart);
        Assert::boolean($isTemplateEnd);
        Assert::allIsInstanceOf($tags, Tag::class);

        $this->summary = $summary;
        $this->description = $description ?: new DocBlock\Description('');
        foreach ($tags as $tag) {
            $this->addTag($tag);
        }

        $this->context = $context;
        $this->location = $location;

        $this->isTemplateEnd = $isTemplateEnd;
        $this->isTemplateStart = $isTemplateStart;
    }

    /**
     * @return string
     */
    public function getSummary()
    {
        return $this->summary;
    }

    /**
     * @return DocBlock\Description
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Returns the current context.
     *
     * @return Types\Context
     */
    public function getContext()
    {
        return $this->context;
    }

    /**
     * Returns the current location.
     *
     * @return Location
     */
    public function getLocation()
    {
        return $this->location;
    }

    /**
     * Returns whether this DocBlock is the start of a Template section.
     *
     * A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker
     * (`#@+`) that is appended directly after the opening `/**` of a DocBlock.
     *
     * An example of such an opening is:
     *
     * ```
     * /**#@+
     *  * My DocBlock
     *  * /
     * ```
     *
     * The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all
     * elements that follow until another DocBlock is found that contains the closing marker (`#@-`).
     *
     * @see self::isTemplateEnd() for the check whether a closing marker was provided.
     *
     * @return boolean
     */
    public function isTemplateStart()
    {
        return $this->isTemplateStart;
    }

    /**
     * Returns whether this DocBlock is the end of a Template section.
     *
     * @see self::isTemplateStart() for a more complete description of the Docblock Template functionality.
     *
     * @return boolean
     */
    public function isTemplateEnd()
    {
        return $this->isTemplateEnd;
    }

    /**
     * Returns the tags for this DocBlock.
     *
     * @return Tag[]
     */
    public function getTags()
    {
        return $this->tags;
    }

    /**
     * Returns an array of tags matching the given name. If no tags are found
     * an empty array is returned.
     *
     * @param string $name String to search by.
     *
     * @return Tag[]
     */
    public function getTagsByName($name)
    {
        Assert::string($name);

        $result = array();

        /** @var Tag $tag */
        foreach ($this->getTags() as $tag) {
            if ($tag->getName() != $name) {
                continue;
            }

            $result[] = $tag;
        }

        return $result;
    }

    /**
     * Checks if a tag of a certain type is present in this DocBlock.
     *
     * @param string $name Tag name to check for.
     *
     * @return bool
     */
    public function hasTag($name)
    {
        Assert::string($name);

        /** @var Tag $tag */
        foreach ($this->getTags() as $tag) {
            if ($tag->getName() == $name) {
                return true;
            }
        }

        return false;
    }

    /**
     * Adds a tag to this DocBlock.
     *
     * @param Tag $tag The tag to add.
     *
     * @return void
     */
    private function addTag(Tag $tag)
    {
        $this->tags[] = $tag;
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection;

use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\DocBlock\StandardTagFactory;
use phpDocumentor\Reflection\DocBlock\Tag;
use phpDocumentor\Reflection\DocBlock\TagFactory;
use Webmozart\Assert\Assert;

final class DocBlockFactory implements DocBlockFactoryInterface
{
    /** @var DocBlock\DescriptionFactory */
    private $descriptionFactory;

    /** @var DocBlock\TagFactory */
    private $tagFactory;

    /**
     * Initializes this factory with the required subcontractors.
     *
     * @param DescriptionFactory $descriptionFactory
     * @param TagFactory         $tagFactory
     */
    public function __construct(DescriptionFactory $descriptionFactory, TagFactory $tagFactory)
    {
        $this->descriptionFactory = $descriptionFactory;
        $this->tagFactory = $tagFactory;
    }

    /**
     * Factory method for easy instantiation.
     *
     * @param string[] $additionalTags
     *
     * @return DocBlockFactory
     */
    public static function createInstance(array $additionalTags = [])
    {
        $fqsenResolver = new FqsenResolver();
        $tagFactory = new StandardTagFactory($fqsenResolver);
        $descriptionFactory = new DescriptionFactory($tagFactory);

        $tagFactory->addService($descriptionFactory);
        $tagFactory->addService(new TypeResolver($fqsenResolver));

        $docBlockFactory = new self($descriptionFactory, $tagFactory);
        foreach ($additionalTags as $tagName => $tagHandler) {
            $docBlockFactory->registerTagHandler($tagName, $tagHandler);
        }

        return $docBlockFactory;
    }

    /**
     * @param object|string $docblock A string containing the DocBlock to parse or an object supporting the
     *                                getDocComment method (such as a ReflectionClass object).
     * @param Types\Context $context
     * @param Location      $location
     *
     * @return DocBlock
     */
    public function create($docblock, Types\Context $context = null, Location $location = null)
    {
        if (is_object($docblock)) {
            if (!method_exists($docblock, 'getDocComment')) {
                $exceptionMessage = 'Invalid object passed; the given object must support the getDocComment method';
                throw new \InvalidArgumentException($exceptionMessage);
            }

            $docblock = $docblock->getDocComment();
        }

        Assert::stringNotEmpty($docblock);

        if ($context === null) {
            $context = new Types\Context('');
        }

        $parts = $this->splitDocBlock($this->stripDocComment($docblock));
        list($templateMarker, $summary, $description, $tags) = $parts;

        return new DocBlock(
            $summary,
            $description ? $this->descriptionFactory->create($description, $context) : null,
            array_filter($this->parseTagBlock($tags, $context), function($tag) {
                return $tag instanceof Tag;
            }),
            $context,
            $location,
            $templateMarker === '#@+',
            $templateMarker === '#@-'
        );
    }

    public function registerTagHandler($tagName, $handler)
    {
        $this->tagFactory->registerTagHandler($tagName, $handler);
    }

    /**
     * Strips the asterisks from the DocBlock comment.
     *
     * @param string $comment String containing the comment text.
     *
     * @return string
     */
    private function stripDocComment($comment)
    {
        $comment = trim(preg_replace('#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]{0,1}(.*)?#u', '$1', $comment));

        // reg ex above is not able to remove */ from a single line docblock
        if (substr($comment, -2) == '*/') {
            $comment = trim(substr($comment, 0, -2));
        }

        return str_replace(array("\r\n", "\r"), "\n", $comment);
    }

    /**
     * Splits the DocBlock into a template marker, summary, description and block of tags.
     *
     * @param string $comment Comment to split into the sub-parts.
     *
     * @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split.
     * @author Mike van Riel <me@mikevanriel.com> for extending the regex with template marker support.
     *
     * @return string[] containing the template marker (if any), summary, description and a string containing the tags.
     */
    private function splitDocBlock($comment)
    {
        // Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This
        // method does not split tags so we return this verbatim as the fourth result (tags). This saves us the
        // performance impact of running a regular expression
        if (strpos($comment, '@') === 0) {
            return array('', '', '', $comment);
        }

        // clears all extra horizontal whitespace from the line endings to prevent parsing issues
        $comment = preg_replace('/\h*$/Sum', '', $comment);

        /*
         * Splits the docblock into a template marker, summary, description and tags section.
         *
         * - The template marker is empty, #@+ or #@- if the DocBlock starts with either of those (a newline may
         *   occur after it and will be stripped).
         * - The short description is started from the first character until a dot is encountered followed by a
         *   newline OR two consecutive newlines (horizontal whitespace is taken into account to consider spacing
         *   errors). This is optional.
         * - The long description, any character until a new line is encountered followed by an @ and word
         *   characters (a tag). This is optional.
         * - Tags; the remaining characters
         *
         * Big thanks to RichardJ for contributing this Regular Expression
         */
        preg_match(
            '/
            \A
            # 1. Extract the template marker
            (?:(\#\@\+|\#\@\-)\n?)?

            # 2. Extract the summary
            (?:
              (?! @\pL ) # The summary may not start with an @
              (
                [^\n.]+
                (?:
                  (?! \. \n | \n{2} )     # End summary upon a dot followed by newline or two newlines
                  [\n.] (?! [ \t]* @\pL ) # End summary when an @ is found as first character on a new line
                  [^\n.]+                 # Include anything else
                )*
                \.?
              )?
            )

            # 3. Extract the description
            (?:
              \s*        # Some form of whitespace _must_ precede a description because a summary must be there
              (?! @\pL ) # The description may not start with an @
              (
                [^\n]+
                (?: \n+
                  (?! [ \t]* @\pL ) # End description when an @ is found as first character on a new line
                  [^\n]+            # Include anything else
                )*
              )
            )?

            # 4. Extract the tags (anything that follows)
            (\s+ [\s\S]*)? # everything that follows
            /ux',
            $comment,
            $matches
        );
        array_shift($matches);

        while (count($matches) < 4) {
            $matches[] = '';
        }

        return $matches;
    }

    /**
     * Creates the tag objects.
     *
     * @param string $tags Tag block to parse.
     * @param Types\Context $context Context of the parsed Tag
     *
     * @return DocBlock\Tag[]
     */
    private function parseTagBlock($tags, Types\Context $context)
    {
        $tags = $this->filterTagBlock($tags);
        if (!$tags) {
            return [];
        }

        $result = $this->splitTagBlockIntoTagLines($tags);
        foreach ($result as $key => $tagLine) {
            $result[$key] = $this->tagFactory->create(trim($tagLine), $context);
        }

        return $result;
    }

    /**
     * @param string $tags
     *
     * @return string[]
     */
    private function splitTagBlockIntoTagLines($tags)
    {
        $result = array();
        foreach (explode("\n", $tags) as $tag_line) {
            if (isset($tag_line[0]) && ($tag_line[0] === '@')) {
                $result[] = $tag_line;
            } else {
                $result[count($result) - 1] .= "\n" . $tag_line;
            }
        }

        return $result;
    }

    /**
     * @param $tags
     * @return string
     */
    private function filterTagBlock($tags)
    {
        $tags = trim($tags);
        if (!$tags) {
            return null;
        }

        if ('@' !== $tags[0]) {
            // @codeCoverageIgnoreStart
            // Can't simulate this; this only happens if there is an error with the parsing of the DocBlock that
            // we didn't foresee.
            throw new \LogicException('A tag block started with text instead of an at-sign(@): ' . $tags);
            // @codeCoverageIgnoreEnd
        }

        return $tags;
    }
}
<?php
namespace phpDocumentor\Reflection;

interface DocBlockFactoryInterface
{
    /**
     * Factory method for easy instantiation.
     *
     * @param string[] $additionalTags
     *
     * @return DocBlockFactory
     */
    public static function createInstance(array $additionalTags = []);

    /**
     * @param string $docblock
     * @param Types\Context $context
     * @param Location $location
     *
     * @return DocBlock
     */
    public function create($docblock, Types\Context $context = null, Location $location = null);
}
<?php

use phpDocumentor\Reflection\TypeResolver;

require '../vendor/autoload.php';

$typeResolver = new TypeResolver();

// Will yield an object of type phpDocumentor\Types\Compound
var_export($typeResolver->resolve('string|integer'));

// Will return the string "string|int"
var_dump((string)$typeResolver->resolve('string|integer'));
<?php

use phpDocumentor\Reflection\Types\Context;
use phpDocumentor\Reflection\TypeResolver;

require '../vendor/autoload.php';

$typeResolver = new TypeResolver();

// Will use the namespace and aliases to resolve to \phpDocumentor\Types\Resolver|Mockery\MockInterface
$context = new Context('\phpDocumentor', [ 'm' => 'Mockery' ]);
var_dump((string)$typeResolver->resolve('Types\Resolver|m\MockInterface', $context));
<?php

use phpDocumentor\Reflection\Types\Context;
use phpDocumentor\Reflection\FqsenResolver;

require '../vendor/autoload.php';

$fqsenResolver = new FqsenResolver();

// Will use the namespace and aliases to resolve to a Fqsen object
$context = new Context('\phpDocumentor\Types');

// Method named: \phpDocumentor\Types\Types\Resolver::resolveFqsen()
var_dump((string)$fqsenResolver->resolve('Types\Resolver::resolveFqsen()', $context));

// Property named: \phpDocumentor\Types\Types\Resolver::$keyWords
var_dump((string)$fqsenResolver->resolve('Types\Resolver::$keyWords', $context));
<?php

use phpDocumentor\Reflection\FqsenResolver;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\ContextFactory;

require '../vendor/autoload.php';
require 'Classy.php';

$typeResolver = new TypeResolver();
$fqsenResolver = new FqsenResolver();

$contextFactory = new ContextFactory();
$context = $contextFactory->createFromReflector(new ReflectionClass('My\\Example\\Classy'));

// Class named: \phpDocumentor\Reflection\Types\Resolver
var_dump((string)$typeResolver->resolve('Types\Resolver', $context));

// String
var_dump((string)$typeResolver->resolve('string', $context));

// Property named: \phpDocumentor\Reflection\Types\Resolver::$keyWords
var_dump((string)$fqsenResolver->resolve('Types\Resolver::$keyWords', $context));

// Class named: \My\Example\string
// - Shows the difference between the FqsenResolver and TypeResolver; the FqsenResolver will assume
//   that the given value is not a type but most definitely a reference to another element. This is
//   because conflicts between type keywords and class names can exist and if you know a reference
//   is not a type but an element you can force that keywords are resolved.
var_dump((string)$fqsenResolver->resolve('string', $context));
<?php

use phpDocumentor\Reflection\FqsenResolver;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\ContextFactory;

require '../vendor/autoload.php';
require 'Classy.php';

$typeResolver = new TypeResolver();
$fqsenResolver = new FqsenResolver();

$contextFactory = new ContextFactory();
$context = $contextFactory->createFromReflector(new ReflectionMethod('My\\Example\\Classy', '__construct'));

// Class named: \phpDocumentor\Reflection\Types\Resolver
var_dump((string)$typeResolver->resolve('Types\Resolver', $context));

// String
var_dump((string)$typeResolver->resolve('string', $context));

// Property named: \phpDocumentor\Reflection\Types\Resolver::$keyWords
var_dump((string)$fqsenResolver->resolve('Types\Resolver::$keyWords', $context));

// Class named: \My\Example\string
// - Shows the difference between the FqsenResolver and TypeResolver; the FqsenResolver will assume
//   that the given value is not a type but most definitely a reference to another element. This is
//   because conflicts between type keywords and class names can exist and if you know a reference
//   is not a type but an element you can force that keywords are resolved.
var_dump((string)$fqsenResolver->resolve('string', $context));
<?php

use phpDocumentor\Reflection\FqsenResolver;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\ContextFactory;

require '../vendor/autoload.php';

$typeResolver = new TypeResolver();
$fqsenResolver = new FqsenResolver();

$contextFactory = new ContextFactory();
$context = $contextFactory->createForNamespace('My\Example', file_get_contents('Classy.php'));

// Class named: \phpDocumentor\Reflection\Types\Resolver
var_dump((string)$typeResolver->resolve('Types\Resolver', $context));

// String
var_dump((string)$typeResolver->resolve('string', $context));

// Property named: \phpDocumentor\Reflection\Types\Resolver::$keyWords
var_dump((string)$fqsenResolver->resolve('Types\Resolver::$keyWords', $context));
<?php

namespace My\Example;

use Mockery as m;
use phpDocumentor\Reflection\Types;

class Classy
{
    /**
     * @var Types\Context
     */
    public function __construct($context)
    {
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection;

use phpDocumentor\Reflection\Types\Context;

class FqsenResolver
{
    /** @var string Definition of the NAMESPACE operator in PHP */
    const OPERATOR_NAMESPACE = '\\';

    public function resolve($fqsen, Context $context = null)
    {
        if ($context === null) {
            $context = new Context('');
        }

        if ($this->isFqsen($fqsen)) {
            return new Fqsen($fqsen);
        }

        return $this->resolvePartialStructuralElementName($fqsen, $context);
    }

    /**
     * Tests whether the given type is a Fully Qualified Structural Element Name.
     *
     * @param string $type
     *
     * @return bool
     */
    private function isFqsen($type)
    {
        return strpos($type, self::OPERATOR_NAMESPACE) === 0;
    }

    /**
     * Resolves a partial Structural Element Name (i.e. `Reflection\DocBlock`) to its FQSEN representation
     * (i.e. `\phpDocumentor\Reflection\DocBlock`) based on the Namespace and aliases mentioned in the Context.
     *
     * @param string $type
     * @param Context $context
     *
     * @return Fqsen
     */
    private function resolvePartialStructuralElementName($type, Context $context)
    {
        $typeParts = explode(self::OPERATOR_NAMESPACE, $type, 2);

        $namespaceAliases = $context->getNamespaceAliases();

        // if the first segment is not an alias; prepend namespace name and return
        if (!isset($namespaceAliases[$typeParts[0]])) {
            $namespace = $context->getNamespace();
            if ('' !== $namespace) {
                $namespace .= self::OPERATOR_NAMESPACE;
            }

            return new Fqsen(self::OPERATOR_NAMESPACE . $namespace . $type);
        }

        $typeParts[0] = $namespaceAliases[$typeParts[0]];

        return new Fqsen(self::OPERATOR_NAMESPACE . implode(self::OPERATOR_NAMESPACE, $typeParts));
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection;

interface Type
{
    public function __toString();
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection;

use phpDocumentor\Reflection\Types\Array_;
use phpDocumentor\Reflection\Types\Compound;
use phpDocumentor\Reflection\Types\Context;
use phpDocumentor\Reflection\Types\Object_;

final class TypeResolver
{
    /** @var string Definition of the ARRAY operator for types */
    const OPERATOR_ARRAY = '[]';

    /** @var string Definition of the NAMESPACE operator in PHP */
    const OPERATOR_NAMESPACE = '\\';

    /** @var string[] List of recognized keywords and unto which Value Object they map */
    private $keywords = array(
        'string' => 'phpDocumentor\Reflection\Types\String_',
        'int' => 'phpDocumentor\Reflection\Types\Integer',
        'integer' => 'phpDocumentor\Reflection\Types\Integer',
        'bool' => 'phpDocumentor\Reflection\Types\Boolean',
        'boolean' => 'phpDocumentor\Reflection\Types\Boolean',
        'float' => 'phpDocumentor\Reflection\Types\Float_',
        'double' => 'phpDocumentor\Reflection\Types\Float_',
        'object' => 'phpDocumentor\Reflection\Types\Object_',
        'mixed' => 'phpDocumentor\Reflection\Types\Mixed',
        'array' => 'phpDocumentor\Reflection\Types\Array_',
        'resource' => 'phpDocumentor\Reflection\Types\Resource',
        'void' => 'phpDocumentor\Reflection\Types\Void_',
        'null' => 'phpDocumentor\Reflection\Types\Null_',
        'scalar' => 'phpDocumentor\Reflection\Types\Scalar',
        'callback' => 'phpDocumentor\Reflection\Types\Callable_',
        'callable' => 'phpDocumentor\Reflection\Types\Callable_',
        'false' => 'phpDocumentor\Reflection\Types\Boolean',
        'true' => 'phpDocumentor\Reflection\Types\Boolean',
        'self' => 'phpDocumentor\Reflection\Types\Self_',
        '$this' => 'phpDocumentor\Reflection\Types\This',
        'static' => 'phpDocumentor\Reflection\Types\Static_'
    );

    /** @var FqsenResolver */
    private $fqsenResolver;

    /**
     * Initializes this TypeResolver with the means to create and resolve Fqsen objects.
     *
     * @param FqsenResolver $fqsenResolver
     */
    public function __construct(FqsenResolver $fqsenResolver = null)
    {
        $this->fqsenResolver = $fqsenResolver ?: new FqsenResolver();
    }

    /**
     * Analyzes the given type and returns the FQCN variant.
     *
     * When a type is provided this method checks whether it is not a keyword or
     * Fully Qualified Class Name. If so it will use the given namespace and
     * aliases to expand the type to a FQCN representation.
     *
     * This method only works as expected if the namespace and aliases are set;
     * no dynamic reflection is being performed here.
     *
     * @param string $type     The relative or absolute type.
     * @param Context $context
     *
     * @uses Context::getNamespace()        to determine with what to prefix the type name.
     * @uses Context::getNamespaceAliases() to check whether the first part of the relative type name should not be
     *     replaced with another namespace.
     *
     * @return Type|null
     */
    public function resolve($type, Context $context = null)
    {
        if (!is_string($type)) {
            throw new \InvalidArgumentException(
                'Attempted to resolve type but it appeared not to be a string, received: ' . var_export($type, true)
            );
        }

        $type = trim($type);
        if (!$type) {
            throw new \InvalidArgumentException('Attempted to resolve "' . $type . '" but it appears to be empty');
        }

        if ($context === null) {
            $context = new Context('');
        }

        switch (true) {
            case $this->isKeyword($type):
                return $this->resolveKeyword($type);
            case ($this->isCompoundType($type)):
                return $this->resolveCompoundType($type, $context);
            case $this->isTypedArray($type):
                return $this->resolveTypedArray($type, $context);
            case $this->isFqsen($type):
                return $this->resolveTypedObject($type);
            case $this->isPartialStructuralElementName($type):
                return $this->resolveTypedObject($type, $context);
            // @codeCoverageIgnoreStart
            default:
                // I haven't got the foggiest how the logic would come here but added this as a defense.
                throw new \RuntimeException(
                    'Unable to resolve type "' . $type . '", there is no known method to resolve it'
                );
        }
        // @codeCoverageIgnoreEnd
    }

    /**
     * Adds a keyword to the list of Keywords and associates it with a specific Value Object.
     *
     * @param string $keyword
     * @param string $typeClassName
     *
     * @return void
     */
    public function addKeyword($keyword, $typeClassName)
    {
        if (!class_exists($typeClassName)) {
            throw new \InvalidArgumentException(
                'The Value Object that needs to be created with a keyword "' . $keyword . '" must be an existing class'
                . ' but we could not find the class ' . $typeClassName
            );
        }

        if (!in_array(Type::class, class_implements($typeClassName))) {
            throw new \InvalidArgumentException(
                'The class "' . $typeClassName . '" must implement the interface "phpDocumentor\Reflection\Type"'
            );
        }

        $this->keywords[$keyword] = $typeClassName;
    }

    /**
     * Detects whether the given type represents an array.
     *
     * @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
     *
     * @return bool
     */
    private function isTypedArray($type)
    {
        return substr($type, -2) === self::OPERATOR_ARRAY;
    }

    /**
     * Detects whether the given type represents a PHPDoc keyword.
     *
     * @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
     *
     * @return bool
     */
    private function isKeyword($type)
    {
        return in_array(strtolower($type), array_keys($this->keywords), true);
    }

    /**
     * Detects whether the given type represents a relative structural element name.
     *
     * @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
     *
     * @return bool
     */
    private function isPartialStructuralElementName($type)
    {
        return ($type[0] !== self::OPERATOR_NAMESPACE) && !$this->isKeyword($type);
    }

    /**
     * Tests whether the given type is a Fully Qualified Structural Element Name.
     *
     * @param string $type
     *
     * @return bool
     */
    private function isFqsen($type)
    {
        return strpos($type, self::OPERATOR_NAMESPACE) === 0;
    }

    /**
     * Tests whether the given type is a compound type (i.e. `string|int`).
     *
     * @param string $type
     *
     * @return bool
     */
    private function isCompoundType($type)
    {
        return strpos($type, '|') !== false;
    }

    /**
     * Resolves the given typed array string (i.e. `string[]`) into an Array object with the right types set.
     *
     * @param string $type
     * @param Context $context
     *
     * @return Array_
     */
    private function resolveTypedArray($type, Context $context)
    {
        return new Array_($this->resolve(substr($type, 0, -2), $context));
    }

    /**
     * Resolves the given keyword (such as `string`) into a Type object representing that keyword.
     *
     * @param string $type
     *
     * @return Type
     */
    private function resolveKeyword($type)
    {
        $className = $this->keywords[strtolower($type)];

        return new $className();
    }

    /**
     * Resolves the given FQSEN string into an FQSEN object.
     *
     * @param string $type
     *
     * @return Object_
     */
    private function resolveTypedObject($type, Context $context = null)
    {
        return new Object_($this->fqsenResolver->resolve($type, $context));
    }

    /**
     * Resolves a compound type (i.e. `string|int`) into the appropriate Type objects or FQSEN.
     *
     * @param string $type
     * @param Context $context
     *
     * @return Compound
     */
    private function resolveCompoundType($type, Context $context)
    {
        $types = [];

        foreach (explode('|', $type) as $part) {
            $types[] = $this->resolve($part, $context);
        }

        return new Compound($types);
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

use phpDocumentor\Reflection\Fqsen;
use phpDocumentor\Reflection\Type;

/**
 * Represents an array type as described in the PSR-5, the PHPDoc Standard.
 *
 * An array can be represented in two forms:
 *
 * 1. Untyped (`array`), where the key and value type is unknown and hence classified as 'Mixed'.
 * 2. Types (`string[]`), where the value type is provided by preceding an opening and closing square bracket with a
 *    type name.
 */
final class Array_ implements Type
{
    /** @var Type */
    private $valueType;

    /** @var Type */
    private $keyType;

    /**
     * Initializes this representation of an array with the given Type or Fqsen.
     *
     * @param Type $valueType
     * @param Type $keyType
     */
    public function __construct(Type $valueType = null, Type $keyType = null)
    {
        if ($keyType === null) {
            $keyType = new Compound([ new String_(), new Integer() ]);
        }
        if ($valueType === null) {
            $valueType = new Mixed();
        }

        $this->valueType = $valueType;
        $this->keyType = $keyType;
    }

    /**
     * Returns the type for the keys of this array.
     *
     * @return Type
     */
    public function getKeyType()
    {
        return $this->keyType;
    }

    /**
     * Returns the value for the keys of this array.
     *
     * @return Type
     */
    public function getValueType()
    {
        return $this->valueType;
    }

    /**
     * Returns a rendered output of the Type as it would be used in a DocBlock.
     *
     * @return string
     */
    public function __toString()
    {
        if ($this->valueType instanceof Mixed) {
            return 'array';
        }

        return $this->valueType . '[]';
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

use phpDocumentor\Reflection\Type;

/**
 * Value Object representing a Boolean type.
 */
final class Boolean implements Type
{
    /**
     * Returns a rendered output of the Type as it would be used in a DocBlock.
     *
     * @return string
     */
    public function __toString()
    {
        return 'bool';
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

use phpDocumentor\Reflection\Type;

/**
 * Value Object representing a Callable type.
 */
final class Callable_ implements Type
{
    /**
     * Returns a rendered output of the Type as it would be used in a DocBlock.
     *
     * @return string
     */
    public function __toString()
    {
        return 'callable';
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

use phpDocumentor\Reflection\Type;

/**
 * Value Object representing a Compound Type.
 *
 * A Compound Type is not so much a special keyword or object reference but is a series of Types that are separated
 * using an OR operator (`|`). This combination of types signifies that whatever is associated with this compound type
 * may contain a value with any of the given types.
 */
final class Compound implements Type
{
    /** @var Type[] */
    private $types = [];

    /**
     * Initializes a compound type (i.e. `string|int`) and tests if the provided types all implement the Type interface.
     *
     * @param Type[] $types
     */
    public function __construct(array $types)
    {
        foreach ($types as $type) {
            if (!$type instanceof Type) {
                throw new \InvalidArgumentException('A compound type can only have other types as elements');
            }
        }

        $this->types = $types;
    }

    /**
     * Returns the type at the given index.
     *
     * @param integer $index
     *
     * @return Type|null
     */
    public function get($index)
    {
        if (!$this->has($index)) {
            return null;
        }

        return $this->types[$index];
    }

    /**
     * Tests if this compound type has a type with the given index.
     *
     * @param integer $index
     *
     * @return bool
     */
    public function has($index)
    {
        return isset($this->types[$index]);
    }

    /**
     * Returns a rendered output of the Type as it would be used in a DocBlock.
     *
     * @return string
     */
    public function __toString()
    {
        return implode('|', $this->types);
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

/**
 * Provides information about the Context in which the DocBlock occurs that receives this context.
 *
 * A DocBlock does not know of its own accord in which namespace it occurs and which namespace aliases are applicable
 * for the block of code in which it is in. This information is however necessary to resolve Class names in tags since
 * you can provide a short form or make use of namespace aliases.
 *
 * The phpDocumentor Reflection component knows how to create this class but if you use the DocBlock parser from your
 * own application it is possible to generate a Context class using the ContextFactory; this will analyze the file in
 * which an associated class resides for its namespace and imports.
 *
 * @see ContextFactory::createFromClassReflector()
 * @see ContextFactory::createForNamespace()
 */
final class Context
{
    /** @var string The current namespace. */
    private $namespace = '';

    /** @var array List of namespace aliases => Fully Qualified Namespace. */
    private $namespaceAliases = [];

    /**
     * Initializes the new context and normalizes all passed namespaces to be in Qualified Namespace Name (QNN)
     * format (without a preceding `\`).
     *
     * @param string $namespace The namespace where this DocBlock resides in.
     * @param array $namespaceAliases List of namespace aliases => Fully Qualified Namespace.
     */
    public function __construct($namespace, array $namespaceAliases = [])
    {
        $this->namespace = ('global' !== $namespace && 'default' !== $namespace)
            ? trim((string)$namespace, '\\')
            : '';

        foreach ($namespaceAliases as $alias => $fqnn) {
            if ($fqnn[0] === '\\') {
                $fqnn = substr($fqnn, 1);
            }
            if ($fqnn[strlen($fqnn) - 1] === '\\') {
                $fqnn = substr($fqnn, 0, -1);
            }

            $namespaceAliases[$alias] = $fqnn;
        }

        $this->namespaceAliases = $namespaceAliases;
    }

    /**
     * Returns the Qualified Namespace Name (thus without `\` in front) where the associated element is in.
     *
     * @return string
     */
    public function getNamespace()
    {
        return $this->namespace;
    }

    /**
     * Returns a list of Qualified Namespace Names (thus without `\` in front) that are imported, the keys represent
     * the alias for the imported Namespace.
     *
     * @return string[]
     */
    public function getNamespaceAliases()
    {
        return $this->namespaceAliases;
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

/**
 * Convenience class to create a Context for DocBlocks when not using the Reflection Component of phpDocumentor.
 *
 * For a DocBlock to be able to resolve types that use partial namespace names or rely on namespace imports we need to
 * provide a bit of context so that the DocBlock can read that and based on it decide how to resolve the types to
 * Fully Qualified names.
 *
 * @see Context for more information.
 */
final class ContextFactory
{
    /** The literal used at the end of a use statement. */
    const T_LITERAL_END_OF_USE = ';';

    /** The literal used between sets of use statements */
    const T_LITERAL_USE_SEPARATOR = ',';

    /**
     * Build a Context given a Class Reflection.
     *
     * @param \ReflectionClass $reflector
     *
     * @see Context for more information on Contexts.
     *
     * @return Context
     */
    public function createFromReflector(\Reflector $reflector)
    {
        if (method_exists($reflector, 'getDeclaringClass')) {
            $reflector = $reflector->getDeclaringClass();
        }

        $fileName = $reflector->getFileName();
        $namespace = $reflector->getNamespaceName();

        if (file_exists($fileName)) {
            return $this->createForNamespace($namespace, file_get_contents($fileName));
        }

        return new Context($namespace, []);
    }

    /**
     * Build a Context for a namespace in the provided file contents.
     *
     * @param string $namespace It does not matter if a `\` precedes the namespace name, this method first normalizes.
     * @param string $fileContents the file's contents to retrieve the aliases from with the given namespace.
     *
     * @see Context for more information on Contexts.
     *
     * @return Context
     */
    public function createForNamespace($namespace, $fileContents)
    {
        $namespace = trim($namespace, '\\');
        $useStatements = [];
        $currentNamespace = '';
        $tokens = new \ArrayIterator(token_get_all($fileContents));

        while ($tokens->valid()) {
            switch ($tokens->current()[0]) {
                case T_NAMESPACE:
                    $currentNamespace = $this->parseNamespace($tokens);
                    break;
                case T_CLASS:
                    // Fast-forward the iterator through the class so that any
                    // T_USE tokens found within are skipped - these are not
                    // valid namespace use statements so should be ignored.
                    $braceLevel = 0;
                    $firstBraceFound = false;
                    while ($tokens->valid() && ($braceLevel > 0 || !$firstBraceFound)) {
                        if ($tokens->current() === '{'
                            || $tokens->current()[0] === T_CURLY_OPEN
                            || $tokens->current()[0] === T_DOLLAR_OPEN_CURLY_BRACES) {
                            if (!$firstBraceFound) {
                                $firstBraceFound = true;
                            }
                            $braceLevel++;
                        }

                        if ($tokens->current() === '}') {
                            $braceLevel--;
                        }
                        $tokens->next();
                    }
                    break;
                case T_USE:
                    if ($currentNamespace === $namespace) {
                        $useStatements = array_merge($useStatements, $this->parseUseStatement($tokens));
                    }
                    break;
            }
            $tokens->next();
        }

        return new Context($namespace, $useStatements);
    }

    /**
     * Deduce the name from tokens when we are at the T_NAMESPACE token.
     *
     * @param \ArrayIterator $tokens
     *
     * @return string
     */
    private function parseNamespace(\ArrayIterator $tokens)
    {
        // skip to the first string or namespace separator
        $this->skipToNextStringOrNamespaceSeparator($tokens);

        $name = '';
        while ($tokens->valid() && ($tokens->current()[0] === T_STRING || $tokens->current()[0] === T_NS_SEPARATOR)
        ) {
            $name .= $tokens->current()[1];
            $tokens->next();
        }

        return $name;
    }

    /**
     * Deduce the names of all imports when we are at the T_USE token.
     *
     * @param \ArrayIterator $tokens
     *
     * @return string[]
     */
    private function parseUseStatement(\ArrayIterator $tokens)
    {
        $uses = [];
        $continue = true;

        while ($continue) {
            $this->skipToNextStringOrNamespaceSeparator($tokens);

            list($alias, $fqnn) = $this->extractUseStatement($tokens);
            $uses[$alias] = $fqnn;
            if ($tokens->current()[0] === self::T_LITERAL_END_OF_USE) {
                $continue = false;
            }
        }

        return $uses;
    }

    /**
     * Fast-forwards the iterator as longs as we don't encounter a T_STRING or T_NS_SEPARATOR token.
     *
     * @param \ArrayIterator $tokens
     *
     * @return void
     */
    private function skipToNextStringOrNamespaceSeparator(\ArrayIterator $tokens)
    {
        while ($tokens->valid() && ($tokens->current()[0] !== T_STRING) && ($tokens->current()[0] !== T_NS_SEPARATOR)) {
            $tokens->next();
        }
    }

    /**
     * Deduce the namespace name and alias of an import when we are at the T_USE token or have not reached the end of
     * a USE statement yet.
     *
     * @param \ArrayIterator $tokens
     *
     * @return string
     */
    private function extractUseStatement(\ArrayIterator $tokens)
    {
        $result = [''];
        while ($tokens->valid()
            && ($tokens->current()[0] !== self::T_LITERAL_USE_SEPARATOR)
            && ($tokens->current()[0] !== self::T_LITERAL_END_OF_USE)
        ) {
            if ($tokens->current()[0] === T_AS) {
                $result[] = '';
            }
            if ($tokens->current()[0] === T_STRING || $tokens->current()[0] === T_NS_SEPARATOR) {
                $result[count($result) - 1] .= $tokens->current()[1];
            }
            $tokens->next();
        }

        if (count($result) == 1) {
            $backslashPos = strrpos($result[0], '\\');

            if (false !== $backslashPos) {
                $result[] = substr($result[0], $backslashPos + 1);
            } else {
                $result[] = $result[0];
            }
        }

        return array_reverse($result);
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

use phpDocumentor\Reflection\Type;

/**
 * Value Object representing a Float.
 */
final class Float_ implements Type
{
    /**
     * Returns a rendered output of the Type as it would be used in a DocBlock.
     *
     * @return string
     */
    public function __toString()
    {
        return 'float';
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

use phpDocumentor\Reflection\Type;

final class Integer implements Type
{
    /**
     * Returns a rendered output of the Type as it would be used in a DocBlock.
     *
     * @return string
     */
    public function __toString()
    {
        return 'int';
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

use phpDocumentor\Reflection\Type;

/**
 * Value Object representing an unknown, or mixed, type.
 */
final class Mixed implements Type
{
    /**
     * Returns a rendered output of the Type as it would be used in a DocBlock.
     *
     * @return string
     */
    public function __toString()
    {
        return 'mixed';
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

use phpDocumentor\Reflection\Type;

/**
 * Value Object representing a null value or type.
 */
final class Null_ implements Type
{
    /**
     * Returns a rendered output of the Type as it would be used in a DocBlock.
     *
     * @return string
     */
    public function __toString()
    {
        return 'null';
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

use phpDocumentor\Reflection\Fqsen;
use phpDocumentor\Reflection\Type;

/**
 * Value Object representing an object.
 *
 * An object can be either typed or untyped. When an object is typed it means that it has an identifier, the FQSEN,
 * pointing to an element in PHP. Object types that are untyped do not refer to a specific class but represent objects
 * in general.
 */
final class Object_ implements Type
{
    /** @var Fqsen|null */
    private $fqsen;

    /**
     * Initializes this object with an optional FQSEN, if not provided this object is considered 'untyped'.
     *
     * @param Fqsen $fqsen
     */
    public function __construct(Fqsen $fqsen = null)
    {
        if (strpos((string)$fqsen, '::') !== false || strpos((string)$fqsen, '()') !== false) {
            throw new \InvalidArgumentException(
                'Object types can only refer to a class, interface or trait but a method, function, constant or '
                . 'property was received: ' . (string)$fqsen
            );
        }

        $this->fqsen = $fqsen;
    }

    /**
     * Returns the FQSEN associated with this object.
     *
     * @return Fqsen|null
     */
    public function getFqsen()
    {
        return $this->fqsen;
    }

    /**
     * Returns a rendered output of the Type as it would be used in a DocBlock.
     *
     * @return string
     */
    public function __toString()
    {
        if ($this->fqsen) {
            return (string)$this->fqsen;
        }

        return 'object';
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

use phpDocumentor\Reflection\Type;

/**
 * Value Object representing the 'resource' Type.
 */
final class Resource implements Type
{
    /**
     * Returns a rendered output of the Type as it would be used in a DocBlock.
     *
     * @return string
     */
    public function __toString()
    {
        return 'resource';
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

use phpDocumentor\Reflection\Type;

/**
 * Value Object representing the 'scalar' pseudo-type, which is either a string, integer, float or boolean.
 */
final class Scalar implements Type
{
    /**
     * Returns a rendered output of the Type as it would be used in a DocBlock.
     *
     * @return string
     */
    public function __toString()
    {
        return 'scalar';
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

use phpDocumentor\Reflection\Type;

/**
 * Value Object representing the 'self' type.
 *
 * Self, as a Type, represents the class in which the associated element was defined.
 */
final class Self_ implements Type
{
    /**
     * Returns a rendered output of the Type as it would be used in a DocBlock.
     *
     * @return string
     */
    public function __toString()
    {
        return 'self';
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

use phpDocumentor\Reflection\Type;

/**
 * Value Object representing the 'static' type.
 *
 * Self, as a Type, represents the class in which the associated element was called. This differs from self as self does
 * not take inheritance into account but static means that the return type is always that of the class of the called
 * element.
 *
 * See the documentation on late static binding in the PHP Documentation for more information on the difference between
 * static and self.
 */
final class Static_ implements Type
{
    /**
     * Returns a rendered output of the Type as it would be used in a DocBlock.
     *
     * @return string
     */
    public function __toString()
    {
        return 'static';
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

use phpDocumentor\Reflection\Type;

/**
 * Value Object representing the type 'string'.
 */
final class String_ implements Type
{
    /**
     * Returns a rendered output of the Type as it would be used in a DocBlock.
     *
     * @return string
     */
    public function __toString()
    {
        return 'string';
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

use phpDocumentor\Reflection\Type;

/**
 * Value Object representing the '$this' pseudo-type.
 *
 * $this, as a Type, represents the instance of the class associated with the element as it was called. $this is
 * commonly used when documenting fluent interfaces since it represents that the same object is returned.
 */
final class This implements Type
{
    /**
     * Returns a rendered output of the Type as it would be used in a DocBlock.
     *
     * @return string
     */
    public function __toString()
    {
        return '$this';
    }
}
<?php
/**
 * This file is part of phpDocumentor.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
 * @link      http://phpdoc.org
 */

namespace phpDocumentor\Reflection\Types;

use phpDocumentor\Reflection\Type;

/**
 * Value Object representing the pseudo-type 'void'.
 *
 * Void is generally only used when working with return types as it signifies that the method intentionally does not
 * return any value.
 */
final class Void_ implements Type
{
    /**
     * Returns a rendered output of the Type as it would be used in a DocBlock.
     *
     * @return string
     */
    public function __toString()
    {
        return 'void';
    }
}
<?php

namespace Fixtures\Prophecy;

class EmptyClass
{
}
<?php

namespace Fixtures\Prophecy;

interface EmptyInterface
{
}
<?php

namespace Fixtures\Prophecy;

final class FinalClass
{
}
<?php

namespace Fixtures\Prophecy;

interface ModifierInterface
{
    public function isAbstract();

    public function getVisibility();
}
<?php

namespace Fixtures\Prophecy;

interface Named
{
    public function getName();
}
<?php

namespace Fixtures\Prophecy;

use I\Simply;

class OptionalDepsClass
{
    public function iHaveAStrangeTypeHintedArg(\I\Simply\Am\Nonexistent $class)
    {
    }

    public function iHaveAnEvenStrangerTypeHintedArg(Simply\Am\Not $class)
    {
    }
}
<?php

namespace Fixtures\Prophecy;

class SpecialMethods
{
    public function __construct()
    {
    }

    function __destruct()
    {
    }

    function __call($name, $arguments)
    {
    }

    function __sleep()
    {
    }

    function __wakeup()
    {
    }

    function __toString()
    {
        return '';
    }

    function __invoke()
    {
    }

}
<?php

namespace Fixtures\Prophecy;

class WithArguments
{
    public function methodWithArgs(array $arg_1 = array(), \ArrayAccess $arg_2, \ArrayAccess $arg_3 = null)
    {
    }
    
    public function methodWithoutTypeHints($arg)
    {
    }
}
<?php

namespace Fixtures\Prophecy;

class WithCallableArgument
{
    public function methodWithArgs(callable $arg_1, callable $arg_2 = null)
    {
    }
}
<?php

namespace Fixtures\Prophecy;

class WithFinalMethod
{
    final public function finalImplementation()
    {
    }
}
<?php

namespace Fixtures\Prophecy;

class WithFinalVirtuallyPrivateMethod
{
    final public function __toString()
    {
        return '';
    }

    final public function _getName()
    {
    }
}
<?php

namespace Fixtures\Prophecy;

abstract class WithProtectedAbstractMethod
{
    abstract protected function innerDetail();
}
<?php

namespace Fixtures\Prophecy;

class WithReferences
{
    public function methodWithReferenceArgument(&$arg_1, \ArrayAccess &$arg_2)
    {
    }
}
<?php

namespace Fixtures\Prophecy;

class WithReturnTypehints extends EmptyClass
{
    public function getSelf(): self {
        return $this;
    }

    public function getName(): string {
        return __CLASS__;
    }
    
    public function getParent(): parent {
        return $this;
    }
}
<?php

namespace Fixtures\Prophecy;

class WithStaticMethod
{
    public static function innerDetail()
    {
    }
}
<?php

namespace Fixtures\Prophecy;

class WithTypehintedVariadicArgument
{
    function methodWithTypeHintedArgs(array ...$args)
    {
    }
}
<?php

namespace Fixtures\Prophecy;

class WithVariadicArgument
{
    function methodWithArgs(...$args)
    {
    }
}
<?php

namespace Fixtures\Prophecy;

class WithVirtuallyPrivateMethod
{
    public function __toString()
    {
        return '';
    }

    public function _getName()
    {
    }

    public function isAbstract()
    {
    }
}
<?php

namespace spec\Prophecy\Argument;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument\Token\TokenInterface;

class ArgumentsWildcardSpec extends ObjectBehavior
{
    function it_wraps_non_token_arguments_into_ExactValueToken(\stdClass $object)
    {
        $this->beConstructedWith(array(42, 'zet', $object));

        $class = get_class($object->getWrappedObject());
        $hash  = spl_object_hash($object->getWrappedObject());

        $this->__toString()->shouldReturn("exact(42), exact(\"zet\"), exact($class:$hash Object (\n    'objectProphecy' => Prophecy\Prophecy\ObjectProphecy Object (*Prophecy*)\n))");
    }

    function it_generates_string_representation_from_all_tokens_imploded(
        TokenInterface $token1,
        TokenInterface $token2,
        TokenInterface $token3
    ) {
        $token1->__toString()->willReturn('token_1');
        $token2->__toString()->willReturn('token_2');
        $token3->__toString()->willReturn('token_3');

        $this->beConstructedWith(array($token1, $token2, $token3));
        $this->__toString()->shouldReturn('token_1, token_2, token_3');
    }

    function it_exposes_list_of_tokens(TokenInterface $token)
    {
        $this->beConstructedWith(array($token));

        $this->getTokens()->shouldReturn(array($token));
    }

    function it_returns_score_of_1_if_there_are_no_tokens_and_arguments()
    {
        $this->beConstructedWith(array());

        $this->scoreArguments(array())->shouldReturn(1);
    }

    function it_should_return_match_score_based_on_all_tokens_score(
        TokenInterface $token1,
        TokenInterface $token2,
        TokenInterface $token3
    ) {
        $token1->scoreArgument('one')->willReturn(3);
        $token1->isLast()->willReturn(false);
        $token2->scoreArgument(2)->willReturn(5);
        $token2->isLast()->willReturn(false);
        $token3->scoreArgument($obj = new \stdClass())->willReturn(10);
        $token3->isLast()->willReturn(false);

        $this->beConstructedWith(array($token1, $token2, $token3));
        $this->scoreArguments(array('one', 2, $obj))->shouldReturn(18);
    }

    function it_returns_false_if_there_is_less_arguments_than_tokens(
        TokenInterface $token1,
        TokenInterface $token2,
        TokenInterface $token3
    ) {
        $token1->scoreArgument('one')->willReturn(3);
        $token1->isLast()->willReturn(false);
        $token2->scoreArgument(2)->willReturn(5);
        $token2->isLast()->willReturn(false);
        $token3->scoreArgument(null)->willReturn(false);
        $token3->isLast()->willReturn(false);

        $this->beConstructedWith(array($token1, $token2, $token3));
        $this->scoreArguments(array('one', 2))->shouldReturn(false);
    }

    function it_returns_false_if_there_is_less_tokens_than_arguments(
        TokenInterface $token1,
        TokenInterface $token2,
        TokenInterface $token3
    ) {
        $token1->scoreArgument('one')->willReturn(3);
        $token1->isLast()->willReturn(false);
        $token2->scoreArgument(2)->willReturn(5);
        $token2->isLast()->willReturn(false);
        $token3->scoreArgument($obj = new \stdClass())->willReturn(10);
        $token3->isLast()->willReturn(false);

        $this->beConstructedWith(array($token1, $token2, $token3));
        $this->scoreArguments(array('one', 2, $obj, 4))->shouldReturn(false);
    }

    function it_should_return_false_if_one_of_the_tokens_returns_false(
        TokenInterface $token1,
        TokenInterface $token2,
        TokenInterface $token3
    ) {
        $token1->scoreArgument('one')->willReturn(3);
        $token1->isLast()->willReturn(false);
        $token2->scoreArgument(2)->willReturn(false);
        $token2->isLast()->willReturn(false);
        $token3->scoreArgument($obj = new \stdClass())->willReturn(10);
        $token3->isLast()->willReturn(false);

        $this->beConstructedWith(array($token1, $token2, $token3));
        $this->scoreArguments(array('one', 2, $obj))->shouldReturn(false);
    }

    function it_should_calculate_score_until_last_token(
        TokenInterface $token1,
        TokenInterface $token2,
        TokenInterface $token3
    ) {
        $token1->scoreArgument('one')->willReturn(3);
        $token1->isLast()->willReturn(false);

        $token2->scoreArgument(2)->willReturn(7);
        $token2->isLast()->willReturn(true);

        $token3->scoreArgument($obj = new \stdClass())->willReturn(10);
        $token3->isLast()->willReturn(false);

        $this->beConstructedWith(array($token1, $token2, $token3));
        $this->scoreArguments(array('one', 2, $obj))->shouldReturn(10);
    }
}
<?php

namespace spec\Prophecy\Argument\Token;

use PhpSpec\ObjectBehavior;

class AnyValuesTokenSpec extends ObjectBehavior
{
    function it_implements_TokenInterface()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Argument\Token\TokenInterface');
    }

    function it_is_last()
    {
        $this->shouldBeLast();
    }

    function its_string_representation_is_star_with_followup()
    {
        $this->__toString()->shouldReturn('* [, ...]');
    }

    function it_scores_any_argument_as_2()
    {
        $this->scoreArgument(42)->shouldReturn(2);
    }
}
<?php

namespace spec\Prophecy\Argument\Token;

use PhpSpec\ObjectBehavior;

class AnyValueTokenSpec extends ObjectBehavior
{
    function it_implements_TokenInterface()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Argument\Token\TokenInterface');
    }

    function it_is_not_last()
    {
        $this->shouldNotBeLast();
    }

    function its_string_representation_is_star()
    {
        $this->__toString()->shouldReturn('*');
    }

    function it_scores_any_argument_as_3()
    {
        $this->scoreArgument(42)->shouldReturn(3);
    }
}
<?php

namespace spec\Prophecy\Argument\Token;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class ApproximateValueTokenSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith(10.12345678, 4);
    }

    function it_is_initializable()
    {
        $this->shouldHaveType('Prophecy\Argument\Token\ApproximateValueToken');
    }

    function it_implements_TokenInterface()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Argument\Token\TokenInterface');
    }

    function it_is_not_last()
    {
        $this->shouldNotBeLast();
    }

    function it_scores_10_if_rounded_argument_matches_rounded_value()
    {
        $this->scoreArgument(10.12345)->shouldReturn(10);
    }

    function it_does_not_score_if_rounded_argument_does_not_match_rounded_value()
    {
        $this->scoreArgument(10.1234)->shouldReturn(false);
    }

    function it_uses_a_default_precision_of_zero()
    {
        $this->beConstructedWith(10.7);
        $this->scoreArgument(11.4)->shouldReturn(10);
    }

    function it_does_not_score_if_rounded_argument_is_not_numeric()
    {
        $this->scoreArgument('hello')->shouldReturn(false);
    }

    function it_has_simple_string_representation()
    {
        $this->__toString()->shouldBe('≅10.1235');
    }
}
<?php

namespace spec\Prophecy\Argument\Token;

use PhpSpec\ObjectBehavior;

class ArrayCountTokenSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith(2);
    }

    function it_implements_TokenInterface()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Argument\Token\TokenInterface');
    }

    function it_is_not_last()
    {
        $this->shouldNotBeLast();
    }

    function it_scores_6_if_argument_array_has_proper_count()
    {
        $this->scoreArgument(array(1,2))->shouldReturn(6);
    }

    function it_scores_6_if_argument_countable_object_has_proper_count(\Countable $countable)
    {
        $countable->count()->willReturn(2);
        $this->scoreArgument($countable)->shouldReturn(6);
    }

    function it_does_not_score_if_argument_is_neither_array_nor_countable_object()
    {
        $this->scoreArgument('string')->shouldBe(false);
        $this->scoreArgument(5)->shouldBe(false);
        $this->scoreArgument(new \stdClass)->shouldBe(false);
    }

    function it_does_not_score_if_argument_array_has_wrong_count()
    {
        $this->scoreArgument(array(1))->shouldReturn(false);
    }

    function it_does_not_score_if_argument_countable_object_has_wrong_count(\Countable $countable)
    {
        $countable->count()->willReturn(3);
        $this->scoreArgument($countable)->shouldReturn(false);
    }

    function it_has_simple_string_representation()
    {
        $this->__toString()->shouldBe('count(2)');
    }

}
<?php

namespace spec\Prophecy\Argument\Token;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Prophecy\Argument\Token\ExactValueToken;
use Prophecy\Argument\Token\TokenInterface;
use Prophecy\Exception\InvalidArgumentException;

class ArrayEntryTokenSpec extends ObjectBehavior
{
    function let(TokenInterface $key, TokenInterface $value)
    {
        $this->beConstructedWith($key, $value);
    }

    function it_implements_TokenInterface()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Argument\Token\TokenInterface');
    }

    function it_is_not_last()
    {
        $this->shouldNotBeLast();
    }

    function it_holds_key_and_value($key, $value)
    {
        $this->getKey()->shouldBe($key);
        $this->getValue()->shouldBe($value);
    }

    function its_string_representation_tells_that_its_an_array_containing_the_key_value_pair($key, $value)
    {
        $key->__toString()->willReturn('key');
        $value->__toString()->willReturn('value');
        $this->__toString()->shouldBe('[..., key => value, ...]');
    }

    function it_wraps_non_token_value_into_ExactValueToken(TokenInterface $key, \stdClass $object)
    {
        $this->beConstructedWith($key, $object);
        $this->getValue()->shouldHaveType('\Prophecy\Argument\Token\ExactValueToken');
    }

    function it_wraps_non_token_key_into_ExactValueToken(\stdClass $object, TokenInterface $value)
    {
        $this->beConstructedWith($object, $value);
        $this->getKey()->shouldHaveType('\Prophecy\Argument\Token\ExactValueToken');
    }

    function it_scores_array_half_of_combined_scores_from_key_and_value_tokens($key, $value)
    {
        $key->scoreArgument('key')->willReturn(4);
        $value->scoreArgument('value')->willReturn(6);
        $this->scoreArgument(array('key'=>'value'))->shouldBe(5);
    }

    function it_scores_traversable_object_half_of_combined_scores_from_key_and_value_tokens(
        TokenInterface $key,
        TokenInterface $value,
        \Iterator $object
    ) {
        $object->current()->will(function () use ($object) {
            $object->valid()->willReturn(false);

            return 'value';
        });
        $object->key()->willReturn('key');
        $object->rewind()->willReturn(null);
        $object->next()->willReturn(null);
        $object->valid()->willReturn(true);
        $key->scoreArgument('key')->willReturn(6);
        $value->scoreArgument('value')->willReturn(2);
        $this->scoreArgument($object)->shouldBe(4);
    }

    function it_throws_exception_during_scoring_of_array_accessible_object_if_key_is_not_ExactValueToken(
        TokenInterface $key,
        TokenInterface $value,
        \ArrayAccess $object
    ) {
        $key->__toString()->willReturn('any_token');
        $this->beConstructedWith($key,$value);
        $errorMessage = 'You can only use exact value tokens to match key of ArrayAccess object'.PHP_EOL.
                        'But you used `any_token`.';
        $this->shouldThrow(new InvalidArgumentException($errorMessage))->duringScoreArgument($object);
    }

    function it_scores_array_accessible_object_half_of_combined_scores_from_key_and_value_tokens(
        ExactValueToken $key,
        TokenInterface $value,
        \ArrayAccess $object
    ) {
        $object->offsetExists('key')->willReturn(true);
        $object->offsetGet('key')->willReturn('value');
        $key->getValue()->willReturn('key');
        $key->scoreArgument('key')->willReturn(3);
        $value->scoreArgument('value')->willReturn(1);
        $this->scoreArgument($object)->shouldBe(2);
    }

    function it_accepts_any_key_token_type_to_score_object_that_is_both_traversable_and_array_accessible(
        TokenInterface $key,
        TokenInterface $value,
        \ArrayIterator $object
    ) {
        $this->beConstructedWith($key, $value);
        $object->current()->will(function () use ($object) {
            $object->valid()->willReturn(false);

            return 'value';
        });
        $object->key()->willReturn('key');
        $object->rewind()->willReturn(null);
        $object->next()->willReturn(null);
        $object->valid()->willReturn(true);
        $this->shouldNotThrow(new InvalidArgumentException)->duringScoreArgument($object);
    }

    function it_does_not_score_if_argument_is_neither_array_nor_traversable_nor_array_accessible()
    {
        $this->scoreArgument('string')->shouldBe(false);
        $this->scoreArgument(new \stdClass)->shouldBe(false);
    }

    function it_does_not_score_empty_array()
    {
        $this->scoreArgument(array())->shouldBe(false);
    }

    function it_does_not_score_array_if_key_and_value_tokens_do_not_score_same_entry($key, $value)
    {
        $argument = array(1 => 'foo', 2 => 'bar');
        $key->scoreArgument(1)->willReturn(true);
        $key->scoreArgument(2)->willReturn(false);
        $value->scoreArgument('foo')->willReturn(false);
        $value->scoreArgument('bar')->willReturn(true);
        $this->scoreArgument($argument)->shouldBe(false);
    }

    function it_does_not_score_traversable_object_without_entries(\Iterator $object)
    {
        $object->rewind()->willReturn(null);
        $object->next()->willReturn(null);
        $object->valid()->willReturn(false);
        $this->scoreArgument($object)->shouldBe(false);
    }

    function it_does_not_score_traversable_object_if_key_and_value_tokens_do_not_score_same_entry(
        TokenInterface $key,
        TokenInterface $value,
        \Iterator $object
    ) {
        $object->current()->willReturn('foo');
        $object->current()->will(function () use ($object) {
            $object->valid()->willReturn(false);

            return 'bar';
        });
        $object->key()->willReturn(1);
        $object->key()->willReturn(2);
        $object->rewind()->willReturn(null);
        $object->next()->willReturn(null);
        $object->valid()->willReturn(true);
        $key->scoreArgument(1)->willReturn(true);
        $key->scoreArgument(2)->willReturn(false);
        $value->scoreArgument('foo')->willReturn(false);
        $value->scoreArgument('bar')->willReturn(true);
        $this->scoreArgument($object)->shouldBe(false);
    }

    function it_does_not_score_array_accessible_object_if_it_has_no_offset_with_key_token_value(
        ExactValueToken $key,
        \ArrayAccess $object
    ) {
        $object->offsetExists('key')->willReturn(false);
        $key->getValue()->willReturn('key');
        $this->scoreArgument($object)->shouldBe(false);
    }

    function it_does_not_score_array_accessible_object_if_key_and_value_tokens_do_not_score_same_entry(
        ExactValueToken $key,
        TokenInterface $value,
        \ArrayAccess $object
    ) {
        $object->offsetExists('key')->willReturn(true);
        $object->offsetGet('key')->willReturn('value');
        $key->getValue()->willReturn('key');
        $value->scoreArgument('value')->willReturn(false);
        $key->scoreArgument('key')->willReturn(true);
        $this->scoreArgument($object)->shouldBe(false);
    }

    function its_score_is_capped_at_8($key, $value)
    {
        $key->scoreArgument('key')->willReturn(10);
        $value->scoreArgument('value')->willReturn(10);
        $this->scoreArgument(array('key'=>'value'))->shouldBe(8);
    }
}
<?php

namespace spec\Prophecy\Argument\Token;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument\Token\TokenInterface;

class ArrayEveryEntryTokenSpec extends ObjectBehavior
{
    function let(TokenInterface $value)
    {
        $this->beConstructedWith($value);
    }

    function it_implements_TokenInterface()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Argument\Token\TokenInterface');
    }

    function it_is_not_last()
    {
        $this->shouldNotBeLast();
    }

    function it_holds_value($value)
    {
        $this->getValue()->shouldBe($value);
    }

    function its_string_representation_tells_that_its_an_array_containing_only_value($value)
    {
        $value->__toString()->willReturn('value');
        $this->__toString()->shouldBe('[value, ..., value]');
    }

    function it_wraps_non_token_value_into_ExactValueToken(\stdClass $stdClass)
    {
        $this->beConstructedWith($stdClass);
        $this->getValue()->shouldHaveType('Prophecy\Argument\Token\ExactValueToken');
    }

    function it_does_not_score_if_argument_is_neither_array_nor_traversable()
    {
        $this->scoreArgument('string')->shouldBe(false);
        $this->scoreArgument(new \stdClass)->shouldBe(false);
    }

    function it_does_not_score_empty_array()
    {
        $this->scoreArgument(array())->shouldBe(false);
    }

    function it_does_not_score_traversable_object_without_entries(\Iterator $object)
    {
        $object->rewind()->willReturn(null);
        $object->next()->willReturn(null);
        $object->valid()->willReturn(false);
        $this->scoreArgument($object)->shouldBe(false);
    }

    function it_scores_avg_of_scores_from_value_tokens($value)
    {
        $value->scoreArgument('value1')->willReturn(6);
        $value->scoreArgument('value2')->willReturn(3);
        $this->scoreArgument(array('value1', 'value2'))->shouldBe(4.5);
    }

    function it_scores_false_if_entry_scores_false($value)
    {
        $value->scoreArgument('value1')->willReturn(6);
        $value->scoreArgument('value2')->willReturn(false);
        $this->scoreArgument(array('value1', 'value2'))->shouldBe(false);
    }

    function it_does_not_score_array_keys($value)
    {
        $value->scoreArgument('value')->willReturn(6);
        $value->scoreArgument('key')->shouldNotBeCalled(0);
        $this->scoreArgument(array('key' => 'value'))->shouldBe(6);
    }

    function it_scores_traversable_object_from_value_token(TokenInterface $value, \Iterator $object)
    {
        $object->current()->will(function ($args, $object) {
            $object->valid()->willReturn(false);

            return 'value';
        });
        $object->key()->willReturn('key');
        $object->rewind()->willReturn(null);
        $object->next()->willReturn(null);
        $object->valid()->willReturn(true);
        $value->scoreArgument('value')->willReturn(2);
        $this->scoreArgument($object)->shouldBe(2);
    }
}
<?php

namespace spec\Prophecy\Argument\Token;

use PhpSpec\ObjectBehavior;

class CallbackTokenSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith('get_class');
    }

    function it_implements_TokenInterface()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Argument\Token\TokenInterface');
    }

    function it_is_not_last()
    {
        $this->shouldNotBeLast();
    }

    function it_scores_7_if_argument_matches_callback()
    {
        $this->beConstructedWith(function ($argument) { return 2 === $argument; });

        $this->scoreArgument(2)->shouldReturn(7);
    }

    function it_does_not_scores_if_argument_does_not_match_callback()
    {
        $this->beConstructedWith(function ($argument) { return 2 === $argument; });

        $this->scoreArgument(5)->shouldReturn(false);
    }

    function its_string_representation_should_tell_that_its_callback()
    {
        $this->__toString()->shouldReturn('callback()');
    }
}
<?php

namespace spec\Prophecy\Argument\Token;

use PhpSpec\ObjectBehavior;

class ExactValueTokenSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith(42);
    }

    function it_implements_TokenInterface()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Argument\Token\TokenInterface');
    }

    function it_is_not_last()
    {
        $this->shouldNotBeLast();
    }

    function it_holds_value()
    {
        $this->getValue()->shouldReturn(42);
    }

    function it_scores_10_if_value_is_equal_to_argument()
    {
        $this->scoreArgument(42)->shouldReturn(10);
        $this->scoreArgument('42')->shouldReturn(10);
    }

    function it_scores_10_if_value_is_an_object_and_equal_to_argument()
    {
        $value = new \DateTime();
        $value2 = clone $value;

        $this->beConstructedWith($value);
        $this->scoreArgument($value2)->shouldReturn(10);
    }

    function it_does_not_scores_if_value_is_not_equal_to_argument()
    {
        $this->scoreArgument(50)->shouldReturn(false);
        $this->scoreArgument(new \stdClass())->shouldReturn(false);
    }

    function it_does_not_scores_if_value_an_object_and_is_not_equal_to_argument()
    {
        $value = new ExactValueTokenFixtureB('ABC');
        $value2 = new ExactValueTokenFixtureB('CBA');

        $this->beConstructedWith($value);
        $this->scoreArgument($value2)->shouldReturn(false);
    }

    function it_does_not_scores_if_value_type_and_is_not_equal_to_argument()
    {
        $this->beConstructedWith(false);
        $this->scoreArgument(0)->shouldReturn(false);
    }

    function it_generates_proper_string_representation_for_integer()
    {
        $this->beConstructedWith(42);
        $this->__toString()->shouldReturn('exact(42)');
    }

    function it_generates_proper_string_representation_for_string()
    {
        $this->beConstructedWith('some string');
        $this->__toString()->shouldReturn('exact("some string")');
    }

    function it_generates_single_line_representation_for_multiline_string()
    {
        $this->beConstructedWith("some\nstring");
        $this->__toString()->shouldReturn('exact("some\\nstring")');
    }

    function it_generates_proper_string_representation_for_double()
    {
        $this->beConstructedWith(42.3);
        $this->__toString()->shouldReturn('exact(42.3)');
    }

    function it_generates_proper_string_representation_for_boolean_true()
    {
        $this->beConstructedWith(true);
        $this->__toString()->shouldReturn('exact(true)');
    }

    function it_generates_proper_string_representation_for_boolean_false()
    {
        $this->beConstructedWith(false);
        $this->__toString()->shouldReturn('exact(false)');
    }

    function it_generates_proper_string_representation_for_null()
    {
        $this->beConstructedWith(null);
        $this->__toString()->shouldReturn('exact(null)');
    }

    function it_generates_proper_string_representation_for_empty_array()
    {
        $this->beConstructedWith(array());
        $this->__toString()->shouldReturn('exact([])');
    }

    function it_generates_proper_string_representation_for_array()
    {
        $this->beConstructedWith(array('zet', 42));
        $this->__toString()->shouldReturn('exact(["zet", 42])');
    }

    function it_generates_proper_string_representation_for_resource()
    {
        $resource = fopen(__FILE__, 'r');
        $this->beConstructedWith($resource);
        $this->__toString()->shouldReturn('exact(stream:'.$resource.')');
    }

    function it_generates_proper_string_representation_for_object(\stdClass $object)
    {
        $objHash = sprintf('%s:%s',
            get_class($object->getWrappedObject()),
            spl_object_hash($object->getWrappedObject())
        );

        $this->beConstructedWith($object);
        $this->__toString()->shouldReturn("exact($objHash Object (\n    'objectProphecy' => Prophecy\Prophecy\ObjectProphecy Object (*Prophecy*)\n))");
    }
}

class ExactValueTokenFixtureA
{
    public $errors;
}

class ExactValueTokenFixtureB extends ExactValueTokenFixtureA
{
    public $errors;
    public $value = null;

    public function __construct($value)
    {
        $this->value = $value;
    }
}
<?php

namespace spec\Prophecy\Argument\Token;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class IdenticalValueTokenSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith(42);
    }

    function it_is_initializable()
    {
        $this->shouldHaveType('Prophecy\Argument\Token\IdenticalValueToken');
    }

    function it_scores_11_if_string_value_is_identical_to_argument()
    {
        $this->beConstructedWith('foo');
        $this->scoreArgument('foo')->shouldReturn(11);
    }

    function it_scores_11_if_boolean_value_is_identical_to_argument()
    {
        $this->beConstructedWith(false);
        $this->scoreArgument(false)->shouldReturn(11);
    }

    function it_scores_11_if_integer_value_is_identical_to_argument()
    {
        $this->beConstructedWith(31);
        $this->scoreArgument(31)->shouldReturn(11);
    }

    function it_scores_11_if_float_value_is_identical_to_argument()
    {
        $this->beConstructedWith(31.12);
        $this->scoreArgument(31.12)->shouldReturn(11);
    }

    function it_scores_11_if_array_value_is_identical_to_argument()
    {
        $this->beConstructedWith(array('foo' => 'bar'));
        $this->scoreArgument(array('foo' => 'bar'))->shouldReturn(11);
    }

    function it_scores_11_if_object_value_is_identical_to_argument()
    {
        $object = new \stdClass();

        $this->beConstructedWith($object);
        $this->scoreArgument($object)->shouldReturn(11);
    }

    function it_scores_false_if_value_is_not_identical_to_argument()
    {
        $this->beConstructedWith(new \stdClass());
        $this->scoreArgument('foo')->shouldReturn(false);
    }

    function it_scores_false_if_object_value_is_not_the_same_instance_than_argument()
    {
        $this->beConstructedWith(new \stdClass());
        $this->scoreArgument(new \stdClass())->shouldReturn(false);
    }

    function it_scores_false_if_integer_value_is_not_identical_to_boolean_argument()
    {
        $this->beConstructedWith(1);
        $this->scoreArgument(true)->shouldReturn(false);
    }

    function it_is_not_last()
    {
        $this->shouldNotBeLast();
    }

    function it_generates_proper_string_representation_for_integer()
    {
        $this->beConstructedWith(42);
        $this->__toString()->shouldReturn('identical(42)');
    }

    function it_generates_proper_string_representation_for_string()
    {
        $this->beConstructedWith('some string');
        $this->__toString()->shouldReturn('identical("some string")');
    }

    function it_generates_single_line_representation_for_multiline_string()
    {
        $this->beConstructedWith("some\nstring");
        $this->__toString()->shouldReturn('identical("some\\nstring")');
    }

    function it_generates_proper_string_representation_for_double()
    {
        $this->beConstructedWith(42.3);
        $this->__toString()->shouldReturn('identical(42.3)');
    }

    function it_generates_proper_string_representation_for_boolean_true()
    {
        $this->beConstructedWith(true);
        $this->__toString()->shouldReturn('identical(true)');
    }

    function it_generates_proper_string_representation_for_boolean_false()
    {
        $this->beConstructedWith(false);
        $this->__toString()->shouldReturn('identical(false)');
    }

    function it_generates_proper_string_representation_for_null()
    {
        $this->beConstructedWith(null);
        $this->__toString()->shouldReturn('identical(null)');
    }

    function it_generates_proper_string_representation_for_empty_array()
    {
        $this->beConstructedWith(array());
        $this->__toString()->shouldReturn('identical([])');
    }

    function it_generates_proper_string_representation_for_array()
    {
        $this->beConstructedWith(array('zet', 42));
        $this->__toString()->shouldReturn('identical(["zet", 42])');
    }

    function it_generates_proper_string_representation_for_resource()
    {
        $resource = fopen(__FILE__, 'r');
        $this->beConstructedWith($resource);
        $this->__toString()->shouldReturn('identical(stream:'.$resource.')');
    }

    function it_generates_proper_string_representation_for_object($object)
    {
        $objHash = sprintf('%s:%s',
            get_class($object->getWrappedObject()),
            spl_object_hash($object->getWrappedObject())
        );

        $this->beConstructedWith($object);
        $this->__toString()->shouldReturn("identical($objHash Object (\n    'objectProphecy' => Prophecy\Prophecy\ObjectProphecy Object (*Prophecy*)\n))");
    }
}
<?php

namespace spec\Prophecy\Argument\Token;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Prophecy\Argument\Token\TokenInterface;

class LogicalAndTokenSpec extends ObjectBehavior
{
    function it_implements_TokenInterface()
    {
        $this->beConstructedWith(array());
        $this->shouldBeAnInstanceOf('Prophecy\Argument\Token\TokenInterface');
    }

    function it_is_not_last()
    {
        $this->beConstructedWith(array());
        $this->shouldNotBeLast();
    }

    function it_generates_string_representation_from_all_tokens_imploded(
        TokenInterface $token1,
        TokenInterface $token2,
        TokenInterface $token3
    ) {
        $token1->__toString()->willReturn('token_1');
        $token2->__toString()->willReturn('token_2');
        $token3->__toString()->willReturn('token_3');

        $this->beConstructedWith(array($token1, $token2, $token3));
        $this->__toString()->shouldReturn('bool(token_1 AND token_2 AND token_3)');
    }

    function it_wraps_non_token_arguments_into_ExactValueToken()
    {
        $this->beConstructedWith(array(15, '1985'));
        $this->__toString()->shouldReturn("bool(exact(15) AND exact(\"1985\"))");
    }

    function it_scores_the_maximum_score_from_all_scores_returned_by_tokens(TokenInterface $token1, TokenInterface $token2)
    {
        $token1->scoreArgument(1)->willReturn(10);
        $token2->scoreArgument(1)->willReturn(5);
        $this->beConstructedWith(array($token1, $token2));
        $this->scoreArgument(1)->shouldReturn(10);
    }

    function it_does_not_score_if_there_are_no_arguments_or_tokens()
    {
        $this->beConstructedWith(array());
        $this->scoreArgument('any')->shouldReturn(false);
    }

    function it_does_not_score_if_either_of_tokens_does_not_score(TokenInterface $token1, TokenInterface $token2)
    {
        $token1->scoreArgument(1)->willReturn(10);
        $token1->scoreArgument(2)->willReturn(false);

        $token2->scoreArgument(1)->willReturn(false);
        $token2->scoreArgument(2)->willReturn(10);

        $this->beConstructedWith(array($token1, $token2));

        $this->scoreArgument(1)->shouldReturn(false);
        $this->scoreArgument(2)->shouldReturn(false);
    }
}
<?php

namespace spec\Prophecy\Argument\Token;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument\Token\TokenInterface;

class LogicalNotTokenSpec extends ObjectBehavior
{
    function let(TokenInterface $token)
    {
        $this->beConstructedWith($token);
    }

    function it_implements_TokenInterface()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Argument\Token\TokenInterface');
    }

    function it_holds_originating_token($token)
    {
        $this->getOriginatingToken()->shouldReturn($token);
    }

    function it_has_simple_string_representation($token)
    {
        $token->__toString()->willReturn('value');
        $this->__toString()->shouldBe('not(value)');
    }

    function it_wraps_non_token_argument_into_ExactValueToken()
    {
        $this->beConstructedWith(5);
        $token = $this->getOriginatingToken();
        $token->shouldhaveType('Prophecy\Argument\Token\ExactValueToken');
        $token->getValue()->shouldBe(5);
    }

    function it_scores_4_if_preset_token_does_not_match_the_argument($token)
    {
        $token->scoreArgument('argument')->willReturn(false);
        $this->scoreArgument('argument')->shouldBe(4);
    }

    function it_does_not_score_if_preset_token_matches_argument($token)
    {
        $token->scoreArgument('argument')->willReturn(5);
        $this->scoreArgument('argument')->shouldBe(false);
    }

    function it_is_last_if_preset_token_is_last($token)
    {
        $token->isLast()->willReturn(true);
        $this->shouldBeLast();
    }

    function it_is_not_last_if_preset_token_is_not_last($token)
    {
        $token->isLast()->willReturn(false);
        $this->shouldNotBeLast();
    }
}
<?php

namespace spec\Prophecy\Argument\Token;

use PhpSpec\ObjectBehavior;

class ObjectStateTokenSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith('getName', 'stdClass');
    }

    function it_implements_TokenInterface()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Argument\Token\TokenInterface');
    }

    function it_is_not_last()
    {
        $this->shouldNotBeLast();
    }

    function it_scores_8_if_argument_object_has_specific_method_state(\ReflectionClass $reflection)
    {
        $reflection->getName()->willReturn('stdClass');

        $this->scoreArgument($reflection)->shouldReturn(8);
    }

    function it_scores_8_if_argument_object_has_specific_property_state(\stdClass $class)
    {
        $class->getName = 'stdClass';

        $this->scoreArgument($class)->shouldReturn(8);
    }

    function it_does_not_score_if_argument_method_state_does_not_match()
    {
        $value = new ObjectStateTokenFixtureB('ABC');
        $value2 = new ObjectStateTokenFixtureB('CBA');

        $this->beConstructedWith('getSelf', $value);
        $this->scoreArgument($value2)->shouldReturn(false);
    }

    function it_does_not_score_if_argument_property_state_does_not_match(\stdClass $class)
    {
        $class->getName = 'SplFileInfo';

        $this->scoreArgument($class)->shouldReturn(false);
    }

    function it_does_not_score_if_argument_object_does_not_have_method_or_property(ObjectStateTokenFixtureA $class)
    {
        $this->scoreArgument($class)->shouldReturn(false);
    }

    function it_does_not_score_if_argument_is_not_object()
    {
        $this->scoreArgument(42)->shouldReturn(false);
    }

    function it_has_simple_string_representation()
    {
        $this->__toString()->shouldReturn('state(getName(), "stdClass")');
    }
}

class ObjectStateTokenFixtureA
{
    public $errors;
}

class ObjectStateTokenFixtureB extends ObjectStateTokenFixtureA
{
    public $errors;
    public $value = null;

    public function __construct($value)
    {
        $this->value = $value;
    }

    public function getSelf()
    {
        return $this;
    }
}
<?php

namespace spec\Prophecy\Argument\Token;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class StringContainsTokenSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith('a substring');
    }

    function it_is_initializable()
    {
        $this->shouldHaveType('Prophecy\Argument\Token\StringContainsToken');
    }

    function it_implements_TokenInterface()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Argument\Token\TokenInterface');
    }

    function it_holds_value()
    {
        $this->getValue()->shouldReturn('a substring');
    }

    function it_is_not_last()
    {
        $this->shouldNotBeLast();
    }

    function it_scores_6_if_the_argument_contains_the_value()
    {
        $this->scoreArgument('Argument containing a substring')->shouldReturn(6);
    }

    function it_does_not_score_if_the_argument_does_not_contain_the_value()
    {
        $this->scoreArgument('Argument will not match')->shouldReturn(false);
    }

    function its_string_representation_shows_substring()
    {
        $this->__toString()->shouldReturn('contains("a substring")');
    }
}
<?php

namespace spec\Prophecy\Argument\Token;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument\Token\TokenInterface;

class TypeTokenSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith('integer');
    }

    function it_implements_TokenInterface()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Argument\Token\TokenInterface');
    }

    function it_is_not_last()
    {
        $this->shouldNotBeLast();
    }

    function it_scores_5_if_argument_matches_simple_type()
    {
        $this->beConstructedWith('integer');

        $this->scoreArgument(42)->shouldReturn(5);
    }

    function it_does_not_scores_if_argument_does_not_match_simple_type()
    {
        $this->beConstructedWith('integer');

        $this->scoreArgument(42.0)->shouldReturn(false);
    }

    function it_scores_5_if_argument_is_an_instance_of_specified_class(\ReflectionObject $object)
    {
        $this->beConstructedWith('ReflectionClass');

        $this->scoreArgument($object)->shouldReturn(5);
    }

    function it_has_simple_string_representation()
    {
        $this->__toString()->shouldReturn('type(integer)');
    }

    function it_scores_5_if_argument_is_an_instance_of_specified_interface(TokenInterface $interface)
    {
        $this->beConstructedWith('Prophecy\Argument\Token\TokenInterface');

        $this->scoreArgument($interface)->shouldReturn(5);
    }
}
<?php

namespace spec\Prophecy;

use PhpSpec\ObjectBehavior;

class ArgumentSpec extends ObjectBehavior
{
    function it_has_a_shortcut_for_exact_argument_token()
    {
        $token = $this->exact(42);
        $token->shouldBeAnInstanceOf('Prophecy\Argument\Token\ExactValueToken');
        $token->getValue()->shouldReturn(42);
    }

    function it_has_a_shortcut_for_any_argument_token()
    {
        $token = $this->any();
        $token->shouldBeAnInstanceOf('Prophecy\Argument\Token\AnyValueToken');
    }

    function it_has_a_shortcut_for_multiple_arguments_token()
    {
        $token = $this->cetera();
        $token->shouldBeAnInstanceOf('Prophecy\Argument\Token\AnyValuesToken');
    }

    function it_has_a_shortcut_for_type_token()
    {
        $token = $this->type('integer');
        $token->shouldBeAnInstanceOf('Prophecy\Argument\Token\TypeToken');
    }

    function it_has_a_shortcut_for_callback_token()
    {
        $token = $this->that('get_class');
        $token->shouldBeAnInstanceOf('Prophecy\Argument\Token\CallbackToken');
    }

    function it_has_a_shortcut_for_object_state_token()
    {
        $token = $this->which('getName', 'everzet');
        $token->shouldBeAnInstanceOf('Prophecy\Argument\Token\ObjectStateToken');
    }

    function it_has_a_shortcut_for_logical_and_token()
    {
        $token = $this->allOf('integer', 5);
        $token->shouldBeAnInstanceOf('Prophecy\Argument\Token\LogicalAndToken');
    }

    function it_has_a_shortcut_for_array_count_token()
    {
        $token = $this->size(5);
        $token->shouldBeAnInstanceOf('Prophecy\Argument\Token\ArrayCountToken');
    }

    function it_has_a_shortcut_for_array_entry_token()
    {
        $token = $this->withEntry('key', 'value');
        $token->shouldBeAnInstanceOf('Prophecy\Argument\Token\ArrayEntryToken');
    }

    function it_has_a_shortcut_for_array_every_entry_token()
    {
        $token = $this->withEveryEntry('value');
        $token->shouldBeAnInstanceOf('Prophecy\Argument\Token\ArrayEveryEntryToken');
    }

    function it_has_a_shortcut_for_identical_value_token()
    {
        $token = $this->is('value');
        $token->shouldBeAnInstanceOf('Prophecy\Argument\Token\IdenticalValueToken');
    }

    function it_has_a_shortcut_for_array_entry_token_matching_any_key()
    {
        $token = $this->containing('value');
        $token->shouldBeAnInstanceOf('Prophecy\Argument\Token\ArrayEntryToken');
        $token->getKey()->shouldHaveType('Prophecy\Argument\Token\AnyValueToken');
    }

    function it_has_a_shortcut_for_array_entry_token_matching_any_value()
    {
        $token = $this->withKey('key');
        $token->shouldBeAnInstanceOf('Prophecy\Argument\Token\ArrayEntryToken');
        $token->getValue()->shouldHaveType('Prophecy\Argument\Token\AnyValueToken');
    }

    function it_has_a_shortcut_for_logical_not_token()
    {
        $token = $this->not('kagux');
        $token->shouldBeAnInstanceOf('Prophecy\Argument\Token\LogicalNotToken');
    }

    function it_has_a_shortcut_for_string_contains_token()
    {
        $token = $this->containingString('string');
        $token->shouldBeAnInstanceOf('Prophecy\Argument\Token\StringContainsToken');
    }

    function it_has_a_shortcut_for_approximate_token()
    {
        $token = $this->approximate(10);
        $token->shouldBeAnInstanceOf('Prophecy\Argument\Token\ApproximateValueToken');
    }
}
<?php

namespace spec\Prophecy\Call;

use PhpSpec\ObjectBehavior;
use Prophecy\Promise\PromiseInterface;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Argument\ArgumentsWildcard;

class CallCenterSpec extends ObjectBehavior
{
    function let(ObjectProphecy $objectProphecy)
    {
    }

    function it_records_calls_made_through_makeCall_method(ObjectProphecy $objectProphecy, ArgumentsWildcard $wildcard)
    {
        $wildcard->scoreArguments(array(5, 2, 3))->willReturn(10);
        $objectProphecy->getMethodProphecies()->willReturn(array());

        $this->makeCall($objectProphecy, 'setValues', array(5, 2, 3));

        $calls = $this->findCalls('setValues', $wildcard);
        $calls->shouldHaveCount(1);

        $calls[0]->shouldBeAnInstanceOf('Prophecy\Call\Call');
        $calls[0]->getMethodName()->shouldReturn('setValues');
        $calls[0]->getArguments()->shouldReturn(array(5, 2, 3));
        $calls[0]->getReturnValue()->shouldReturn(null);
    }

    function it_returns_null_for_any_call_through_makeCall_if_no_method_prophecies_added(
        $objectProphecy
    )
    {
        $objectProphecy->getMethodProphecies()->willReturn(array());

        $this->makeCall($objectProphecy, 'setValues', array(5, 2, 3))->shouldReturn(null);
    }

    function it_executes_promise_of_method_prophecy_that_matches_signature_passed_to_makeCall(
        $objectProphecy,
        MethodProphecy $method1,
        MethodProphecy $method2,
        MethodProphecy $method3,
        ArgumentsWildcard $arguments1,
        ArgumentsWildcard $arguments2,
        ArgumentsWildcard $arguments3,
        PromiseInterface $promise
    ) {
        $method1->hasReturnVoid()->willReturn(false);
        $method1->getMethodName()->willReturn('getName');
        $method1->getArgumentsWildcard()->willReturn($arguments1);
        $arguments1->scoreArguments(array('world', 'everything'))->willReturn(false);

        $method2->hasReturnVoid()->willReturn(false);
        $method2->getMethodName()->willReturn('setTitle');
        $method2->getArgumentsWildcard()->willReturn($arguments2);
        $arguments2->scoreArguments(array('world', 'everything'))->willReturn(false);

        $method3->hasReturnVoid()->willReturn(false);
        $method3->getMethodName()->willReturn('getName');
        $method3->getArgumentsWildcard()->willReturn($arguments3);
        $method3->getPromise()->willReturn($promise);
        $arguments3->scoreArguments(array('world', 'everything'))->willReturn(200);

        $objectProphecy->getMethodProphecies()->willReturn(array(
            'method1' => array($method1),
            'method2' => array($method2, $method3)
        ));
        $objectProphecy->getMethodProphecies('getName')->willReturn(array($method1, $method3));
        $objectProphecy->reveal()->willReturn(new \stdClass());

        $promise->execute(array('world', 'everything'), $objectProphecy->getWrappedObject(), $method3)->willReturn(42);

        $this->makeCall($objectProphecy, 'getName', array('world', 'everything'))->shouldReturn(42);

        $calls = $this->findCalls('getName', $arguments3);
        $calls->shouldHaveCount(1);
        $calls[0]->getReturnValue()->shouldReturn(42);
    }

    function it_executes_promise_of_method_prophecy_that_matches_with_highest_score_to_makeCall(
        $objectProphecy,
        MethodProphecy $method1,
        MethodProphecy $method2,
        MethodProphecy $method3,
        ArgumentsWildcard $arguments1,
        ArgumentsWildcard $arguments2,
        ArgumentsWildcard $arguments3,
        PromiseInterface $promise
    ) {
        $method1->hasReturnVoid()->willReturn(false);
        $method1->getMethodName()->willReturn('getName');
        $method1->getArgumentsWildcard()->willReturn($arguments1);
        $arguments1->scoreArguments(array('world', 'everything'))->willReturn(50);

        $method2->hasReturnVoid()->willReturn(false);
        $method2->getMethodName()->willReturn('getName');
        $method2->getArgumentsWildcard()->willReturn($arguments2);
        $method2->getPromise()->willReturn($promise);
        $arguments2->scoreArguments(array('world', 'everything'))->willReturn(300);

        $method3->hasReturnVoid()->willReturn(false);
        $method3->getMethodName()->willReturn('getName');
        $method3->getArgumentsWildcard()->willReturn($arguments3);
        $arguments3->scoreArguments(array('world', 'everything'))->willReturn(200);

        $objectProphecy->getMethodProphecies()->willReturn(array(
            'method1' => array($method1),
            'method2' => array($method2, $method3)
        ));
        $objectProphecy->getMethodProphecies('getName')->willReturn(array(
            $method1, $method2, $method3
        ));
        $objectProphecy->reveal()->willReturn(new \stdClass());

        $promise->execute(array('world', 'everything'), $objectProphecy->getWrappedObject(), $method2)
            ->willReturn('second');

        $this->makeCall($objectProphecy, 'getName', array('world', 'everything'))
            ->shouldReturn('second');
    }

    function it_throws_exception_if_call_does_not_match_any_of_defined_method_prophecies(
        $objectProphecy,
        MethodProphecy $method,
        ArgumentsWildcard $arguments
    ) {
        $method->getMethodName()->willReturn('getName');
        $method->getArgumentsWildcard()->willReturn($arguments);
        $arguments->scoreArguments(array('world', 'everything'))->willReturn(false);
        $arguments->__toString()->willReturn('arg1, arg2');

        $objectProphecy->getMethodProphecies()->willReturn(array('method1' => array($method)));
        $objectProphecy->getMethodProphecies('getName')->willReturn(array($method));

        $this->shouldThrow('Prophecy\Exception\Call\UnexpectedCallException')
            ->duringMakeCall($objectProphecy, 'getName', array('world', 'everything'));
    }

    function it_returns_null_if_method_prophecy_that_matches_makeCall_arguments_has_no_promise(
        $objectProphecy,
        MethodProphecy $method,
        ArgumentsWildcard $arguments
    ) {
        $method->hasReturnVoid()->willReturn(false);
        $method->getMethodName()->willReturn('getName');
        $method->getArgumentsWildcard()->willReturn($arguments);
        $method->getPromise()->willReturn(null);
        $arguments->scoreArguments(array('world', 'everything'))->willReturn(100);

        $objectProphecy->getMethodProphecies()->willReturn(array($method));
        $objectProphecy->getMethodProphecies('getName')->willReturn(array($method));

        $this->makeCall($objectProphecy, 'getName', array('world', 'everything'))
            ->shouldReturn(null);
    }

    function it_finds_recorded_calls_by_a_method_name_and_arguments_wildcard(
        $objectProphecy,
        ArgumentsWildcard $wildcard
    ) {
        $objectProphecy->getMethodProphecies()->willReturn(array());

        $this->makeCall($objectProphecy, 'getName', array('world'));
        $this->makeCall($objectProphecy, 'getName', array('everything'));
        $this->makeCall($objectProphecy, 'setName', array(42));

        $wildcard->scoreArguments(array('world'))->willReturn(false);
        $wildcard->scoreArguments(array('everything'))->willReturn(10);

        $calls = $this->findCalls('getName', $wildcard);

        $calls->shouldHaveCount(1);
        $calls[0]->getMethodName()->shouldReturn('getName');
        $calls[0]->getArguments()->shouldReturn(array('everything'));
    }
}
<?php

namespace spec\Prophecy\Call;

use PhpSpec\ObjectBehavior;

class CallSpec extends ObjectBehavior
{
    function let(\Exception $exception)
    {
        $this->beConstructedWith('setValues', array(5, 2), 42, $exception, 'some_file.php', 23);
    }

    function it_exposes_method_name_through_getter()
    {
        $this->getMethodName()->shouldReturn('setValues');
    }

    function it_exposes_arguments_through_getter()
    {
        $this->getArguments()->shouldReturn(array(5, 2));
    }

    function it_exposes_return_value_through_getter()
    {
        $this->getReturnValue()->shouldReturn(42);
    }

    function it_exposes_exception_through_getter($exception)
    {
        $this->getException()->shouldReturn($exception);
    }

    function it_exposes_file_and_line_through_getter()
    {
        $this->getFile()->shouldReturn('some_file.php');
        $this->getLine()->shouldReturn(23);
    }

    function it_returns_shortpath_to_callPlace()
    {
        $this->getCallPlace()->shouldReturn('some_file.php:23');
    }

    function it_returns_unknown_as_callPlace_if_no_file_or_line_provided()
    {
        $this->beConstructedWith('setValues', array(), 0, null, null, null);

        $this->getCallPlace()->shouldReturn('unknown');
    }
}
<?php

namespace spec\Prophecy\Comparator;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class ClosureComparatorSpec extends ObjectBehavior
{
    function it_is_comparator()
    {
        $this->shouldHaveType('SebastianBergmann\Comparator\Comparator');
    }

    function it_accepts_only_closures()
    {
        $this->accepts(123, 321)->shouldReturn(false);
        $this->accepts('string', 'string')->shouldReturn(false);
        $this->accepts(false, true)->shouldReturn(false);
        $this->accepts(true, false)->shouldReturn(false);
        $this->accepts((object)array(), (object)array())->shouldReturn(false);
        $this->accepts(function(){}, (object)array())->shouldReturn(false);
        $this->accepts(function(){}, (object)array())->shouldReturn(false);

        $this->accepts(function(){}, function(){})->shouldReturn(true);
    }

    function it_asserts_that_all_closures_are_different()
    {
        $this->shouldThrow()->duringAssertEquals(function(){}, function(){});
    }

    function it_asserts_that_all_closures_are_different_even_if_its_the_same_closure()
    {
        $closure = function(){};

        $this->shouldThrow()->duringAssertEquals($closure, $closure);
    }
}
<?php

namespace spec\Prophecy\Comparator;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class FactorySpec extends ObjectBehavior
{
    function it_extends_Sebastian_Comparator_Factory()
    {
        $this->shouldHaveType('SebastianBergmann\Comparator\Factory');
    }

    function it_should_have_ClosureComparator_registered()
    {
        $comparator = $this->getInstance()->getComparatorFor(function(){}, function(){});
        $comparator->shouldHaveType('Prophecy\Comparator\ClosureComparator');
    }
}
<?php

namespace spec\Prophecy\Comparator;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Prophecy\Prophet;

class ProphecyComparatorSpec extends ObjectBehavior
{
    function it_is_a_comparator()
    {
        $this->shouldHaveType('SebastianBergmann\Comparator\ObjectComparator');
    }

    function it_accepts_only_prophecy_objects()
    {
        $this->accepts(123, 321)->shouldReturn(false);
        $this->accepts('string', 'string')->shouldReturn(false);
        $this->accepts(false, true)->shouldReturn(false);
        $this->accepts(true, false)->shouldReturn(false);
        $this->accepts((object)array(), (object)array())->shouldReturn(false);
        $this->accepts(function(){}, (object)array())->shouldReturn(false);
        $this->accepts(function(){}, function(){})->shouldReturn(false);

        $prophet = new Prophet();
        $prophecy = $prophet->prophesize('Prophecy\Prophecy\ObjectProphecy');

        $this->accepts($prophecy, $prophecy)->shouldReturn(true);
    }

    function it_asserts_that_an_object_is_equal_to_its_revealed_prophecy()
    {
        $prophet = new Prophet();
        $prophecy = $prophet->prophesize('Prophecy\Prophecy\ObjectProphecy');

        $this->shouldNotThrow()->duringAssertEquals($prophecy->reveal(), $prophecy);
    }
}
<?php

namespace spec\Prophecy\Doubler\ClassPatch;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Prophecy\Doubler\Generator\Node\ArgumentNode;
use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;

class DisableConstructorPatchSpec extends ObjectBehavior
{
    function it_is_a_patch()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Doubler\ClassPatch\ClassPatchInterface');
    }

    function its_priority_is_100()
    {
        $this->getPriority()->shouldReturn(100);
    }

    function it_supports_anything(ClassNode $node)
    {
        $this->supports($node)->shouldReturn(true);
    }

    function it_makes_all_constructor_arguments_optional(
        ClassNode $class,
        MethodNode $method,
        ArgumentNode $arg1,
        ArgumentNode $arg2
    ) {
        $class->hasMethod('__construct')->willReturn(true);
        $class->getMethod('__construct')->willReturn($method);
        $method->getArguments()->willReturn(array($arg1, $arg2));

        $arg1->setDefault(null)->shouldBeCalled();
        $arg2->setDefault(null)->shouldBeCalled();

        $method->setCode(Argument::type('string'))->shouldBeCalled();

        $this->apply($class);
    }

    function it_creates_new_constructor_if_object_has_none(ClassNode $class)
    {
        $class->hasMethod('__construct')->willReturn(false);
        $class->addMethod(Argument::type('Prophecy\Doubler\Generator\Node\MethodNode'))
            ->shouldBeCalled();

        $this->apply($class);
    }
}
<?php

namespace spec\Prophecy\Doubler\ClassPatch;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;

class HhvmExceptionPatchSpec extends ObjectBehavior
{
    function it_is_a_patch()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Doubler\ClassPatch\ClassPatchInterface');
    }

    function its_priority_is_minus_50()
    {
        $this->getPriority()->shouldReturn(-50);
    }

    function it_uses_parent_code_for_setTraceOptions(ClassNode $node, MethodNode $method, MethodNode $getterMethod)
    {
        $node->hasMethod('setTraceOptions')->willReturn(true);
        $node->getMethod('setTraceOptions')->willReturn($method);
        $node->hasMethod('getTraceOptions')->willReturn(true);
        $node->getMethod('getTraceOptions')->willReturn($getterMethod);

        $method->useParentCode()->shouldBeCalled();
        $getterMethod->useParentCode()->shouldBeCalled();

        $this->apply($node);
    }
}
<?php

namespace spec\Prophecy\Doubler\ClassPatch;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;

class KeywordPatchSpec extends ObjectBehavior
{
    function it_is_a_patch()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Doubler\ClassPatch\ClassPatchInterface');
    }

    function its_priority_is_49()
    {
        $this->getPriority()->shouldReturn(49);
    }

    function it_will_remove_echo_and_eval_methods(
        ClassNode $node,
        MethodNode $method1,
        MethodNode $method2,
        MethodNode $method3
    ) {
        $node->removeMethod('eval')->shouldBeCalled();
        $node->removeMethod('echo')->shouldBeCalled();

        $method1->getName()->willReturn('echo');
        $method2->getName()->willReturn('eval');
        $method3->getName()->willReturn('notKeyword');

        $node->getMethods()->willReturn(array(
            'echo' => $method1,
            'eval' => $method2,
            'notKeyword' => $method3,
        ));

        $this->apply($node);
    }
}
<?php

namespace spec\Prophecy\Doubler\ClassPatch;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;

class MagicCallPatchSpec extends ObjectBehavior
{
    function it_is_a_patch()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Doubler\ClassPatch\ClassPatchInterface');
    }

    function it_supports_anything(ClassNode $node)
    {
        $this->supports($node)->shouldReturn(true);
    }

    function it_discovers_api_using_phpdoc(ClassNode $node)
    {
        $node->getParentClass()->willReturn('spec\Prophecy\Doubler\ClassPatch\MagicalApi');
        $node->getInterfaces()->willReturn(array());

        $node->addMethod(new MethodNode('undefinedMethod'))->shouldBeCalled();

        $this->apply($node);
    }

    function it_ignores_existing_methods(ClassNode $node)
    {
        $node->getParentClass()->willReturn('spec\Prophecy\Doubler\ClassPatch\MagicalApiExtended');
        $node->getInterfaces()->willReturn(array());

        $node->addMethod(new MethodNode('undefinedMethod'))->shouldBeCalled();
        $node->addMethod(new MethodNode('definedMethod'))->shouldNotBeCalled();

        $this->apply($node);
    }

    function it_ignores_empty_methods_from_phpdoc(ClassNode $node)
    {
        $node->getParentClass()->willReturn('spec\Prophecy\Doubler\ClassPatch\MagicalApiInvalidMethodDefinition');
        $node->getInterfaces()->willReturn(array());

        $node->addMethod(new MethodNode(''))->shouldNotBeCalled();

        $this->apply($node);
    }

    function it_discovers_api_using_phpdoc_from_implemented_interfaces(ClassNode $node)
    {
        $node->getParentClass()->willReturn('spec\Prophecy\Doubler\ClassPatch\MagicalApiImplemented');
        $node->getInterfaces()->willReturn(array());

        $node->addMethod(new MethodNode('implementedMethod'))->shouldBeCalled();

        $this->apply($node);
    }

    function it_discovers_api_using_phpdoc_from_own_interfaces(ClassNode $node)
    {
        $node->getParentClass()->willReturn('stdClass');
        $node->getInterfaces()->willReturn(array('spec\Prophecy\Doubler\ClassPatch\MagicalApiImplemented'));

        $node->addMethod(new MethodNode('implementedMethod'))->shouldBeCalled();

        $this->apply($node);
    }

    function it_discovers_api_using_phpdoc_from_extended_parent_interfaces(ClassNode $node)
    {
        $node->getParentClass()->willReturn('spec\Prophecy\Doubler\ClassPatch\MagicalApiImplementedExtended');
        $node->getInterfaces()->willReturn(array());

        $node->addMethod(new MethodNode('implementedMethod'))->shouldBeCalled();

        $this->apply($node);
    }

    function it_has_50_priority()
    {
        $this->getPriority()->shouldReturn(50);
    }
}

/**
 * @method void undefinedMethod()
 */
class MagicalApi
{
    /**
     * @return void
     */
    public function definedMethod()
    {

    }
}

/**
 * @method void invalidMethodDefinition
 * @method void
 * @method
 */
class MagicalApiInvalidMethodDefinition
{
}

/**
 * @method void undefinedMethod()
 * @method void definedMethod()
 */
class MagicalApiExtended extends MagicalApi
{

}

/**
 */
class MagicalApiImplemented implements MagicalApiInterface
{

}

/**
 */
class MagicalApiImplementedExtended extends MagicalApiImplemented
{
}

/**
 * @method void implementedMethod()
 */
interface MagicalApiInterface
{

}
<?php

namespace spec\Prophecy\Doubler\ClassPatch;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;

class ProphecySubjectPatchSpec extends ObjectBehavior
{
    function it_is_a_patch()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Doubler\ClassPatch\ClassPatchInterface');
    }

    function it_has_priority_of_0()
    {
        $this->getPriority()->shouldReturn(0);
    }

    function it_supports_any_class(ClassNode $node)
    {
        $this->supports($node)->shouldReturn(true);
    }

    function it_forces_class_to_implement_ProphecySubjectInterface(ClassNode $node)
    {
        $node->addInterface('Prophecy\Prophecy\ProphecySubjectInterface')->shouldBeCalled();

        $node->addProperty('objectProphecy', 'private')->willReturn(null);
        $node->getMethods()->willReturn(array());
        $node->hasMethod(Argument::any())->willReturn(false);
        $node->addMethod(Argument::type('Prophecy\Doubler\Generator\Node\MethodNode'))->willReturn(null);
        $node->addMethod(Argument::type('Prophecy\Doubler\Generator\Node\MethodNode'))->willReturn(null);

        $this->apply($node);
    }

    function it_forces_all_class_methods_except_constructor_to_proxy_calls_into_prophecy_makeCall(
        ClassNode $node,
        MethodNode $constructor,
        MethodNode $method1,
        MethodNode $method2,
        MethodNode $method3
    ) {
        $node->addInterface('Prophecy\Prophecy\ProphecySubjectInterface')->willReturn(null);
        $node->addProperty('objectProphecy', 'private')->willReturn(null);
        $node->hasMethod(Argument::any())->willReturn(false);
        $node->addMethod(Argument::type('Prophecy\Doubler\Generator\Node\MethodNode'))->willReturn(null);
        $node->addMethod(Argument::type('Prophecy\Doubler\Generator\Node\MethodNode'))->willReturn(null);

        $constructor->getName()->willReturn('__construct');
        $method1->getName()->willReturn('method1');
        $method2->getName()->willReturn('method2');
        $method3->getName()->willReturn('method3');

        $method1->getReturnType()->willReturn('int');
        $method2->getReturnType()->willReturn('int');
        $method3->getReturnType()->willReturn('void');

        $node->getMethods()->willReturn(array(
            'method1' => $method1,
            'method2' => $method2,
            'method3' => $method3,
        ));

        $constructor->setCode(Argument::any())->shouldNotBeCalled();

        $method1->setCode('return $this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());')
            ->shouldBeCalled();
        $method2->setCode('return $this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());')
            ->shouldBeCalled();
        $method3->setCode('$this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());')
            ->shouldBeCalled();

        $this->apply($node);
    }
}
<?php

namespace spec\Prophecy\Doubler\ClassPatch;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Prophecy\Doubler\Generator\Node\ArgumentNode;
use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;

class ReflectionClassNewInstancePatchSpec extends ObjectBehavior
{
    function it_is_a_patch()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Doubler\ClassPatch\ClassPatchInterface');
    }

    function its_priority_is_50()
    {
        $this->getPriority()->shouldReturn(50);
    }

    function it_supports_ReflectionClass_only(ClassNode $reflectionClassNode, ClassNode $anotherClassNode)
    {
        $reflectionClassNode->getParentClass()->willReturn('ReflectionClass');
        $anotherClassNode->getParentClass()->willReturn('stdClass');

        $this->supports($reflectionClassNode)->shouldReturn(true);
        $this->supports($anotherClassNode)->shouldReturn(false);
    }

    function it_makes_all_newInstance_arguments_optional(
        ClassNode $class,
        MethodNode $method,
        ArgumentNode $arg1
    ) {
        $class->getMethod('newInstance')->willReturn($method);
        $method->getArguments()->willReturn(array($arg1));
        $arg1->setDefault(null)->shouldBeCalled();

        $this->apply($class);
    }
}
<?php

namespace spec\Prophecy\Doubler\ClassPatch;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;

class SplFileInfoPatchSpec extends ObjectBehavior
{
    function it_is_a_patch()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Doubler\ClassPatch\ClassPatchInterface');
    }

    function its_priority_is_50()
    {
        $this->getPriority()->shouldReturn(50);
    }

    function it_does_not_support_nodes_without_parent_class(ClassNode $node)
    {
        $node->getParentClass()->willReturn('stdClass');
        $this->supports($node)->shouldReturn(false);
    }

    function it_supports_nodes_with_SplFileInfo_as_parent_class(ClassNode $node)
    {
        $node->getParentClass()->willReturn('SplFileInfo');
        $this->supports($node)->shouldReturn(true);
    }

    function it_supports_nodes_with_derivative_of_SplFileInfo_as_parent_class(ClassNode $node)
    {
        $node->getParentClass()->willReturn('SplFileInfo');
        $this->supports($node)->shouldReturn(true);
    }

    function it_adds_a_method_to_node_if_not_exists(ClassNode $node)
    {
        $node->hasMethod('__construct')->willReturn(false);
        $node->addMethod(Argument::any())->shouldBeCalled();
        $node->getParentClass()->shouldBeCalled();

        $this->apply($node);
    }

    function it_updates_existing_method_if_found(ClassNode $node, MethodNode $method)
    {
        $node->hasMethod('__construct')->willReturn(true);
        $node->getMethod('__construct')->willReturn($method);
        $node->getParentClass()->shouldBeCalled();

        $method->useParentCode()->shouldBeCalled();

        $this->apply($node);
    }

    function it_should_not_supply_a_file_for_a_directory_iterator(ClassNode $node, MethodNode $method)
    {
        $node->hasMethod('__construct')->willReturn(true);
        $node->getMethod('__construct')->willReturn($method);
        $node->getParentClass()->willReturn('DirectoryIterator');

        $method->setCode(Argument::that(function($value) {
            return strpos($value, '.php') === false;
        }))->shouldBeCalled();

        $this->apply($node);
    }

    function it_should_supply_a_file_for_a_spl_file_object(ClassNode $node, MethodNode $method)
    {
        $node->hasMethod('__construct')->willReturn(true);
        $node->getMethod('__construct')->willReturn($method);
        $node->getParentClass()->willReturn('SplFileObject');

        $method->setCode(Argument::that(function($value) {
            return strpos($value, '.php') !== false;
        }))->shouldBeCalled();

        $this->apply($node);
    }
}
<?php

namespace spec\Prophecy\Doubler\ClassPatch;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Prophecy\Doubler\Generator\Node\ClassNode;

class TraversablePatchSpec extends ObjectBehavior
{
    function it_is_a_patch()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Doubler\ClassPatch\ClassPatchInterface');
    }

    function it_supports_class_that_implements_only_Traversable(ClassNode $node)
    {
        $node->getInterfaces()->willReturn(array('Traversable'));

        $this->supports($node)->shouldReturn(true);
    }

    function it_does_not_support_class_that_implements_Iterator(ClassNode $node)
    {
        $node->getInterfaces()->willReturn(array('Traversable', 'Iterator'));

        $this->supports($node)->shouldReturn(false);
    }

    function it_does_not_support_class_that_implements_IteratorAggregate(ClassNode $node)
    {
        $node->getInterfaces()->willReturn(array('Traversable', 'IteratorAggregate'));

        $this->supports($node)->shouldReturn(false);
    }

    function it_has_100_priority()
    {
        $this->getPriority()->shouldReturn(100);
    }

    function it_forces_node_to_implement_IteratorAggregate(ClassNode $node)
    {
        $node->addInterface('Iterator')->shouldBeCalled();

        $node->addMethod(Argument::type('Prophecy\Doubler\Generator\Node\MethodNode'))->willReturn(null);

        $this->apply($node);
    }
}
<?php

namespace spec\Prophecy\Doubler;

use PhpSpec\ObjectBehavior;
use Prophecy\Doubler\ClassPatch\ClassPatchInterface;
use Prophecy\Doubler\Generator\ClassCreator;
use Prophecy\Doubler\Generator\ClassMirror;
use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\NameGenerator;

class DoublerSpec extends ObjectBehavior
{
    function let(ClassMirror $mirror, ClassCreator $creator, NameGenerator $namer)
    {
        $this->beConstructedWith($mirror, $creator, $namer);
    }

    function it_does_not_have_patches_by_default()
    {
        $this->getClassPatches()->shouldHaveCount(0);
    }

    function its_registerClassPatch_adds_a_patch_to_the_doubler(ClassPatchInterface $patch)
    {
        $this->registerClassPatch($patch);
        $this->getClassPatches()->shouldReturn(array($patch));
    }

    function its_getClassPatches_sorts_patches_by_priority(
        ClassPatchInterface $alt1,
        ClassPatchInterface $alt2,
        ClassPatchInterface $alt3,
        ClassPatchInterface $alt4
    ) {
        $alt1->getPriority()->willReturn(2);
        $alt2->getPriority()->willReturn(50);
        $alt3->getPriority()->willReturn(10);
        $alt4->getPriority()->willReturn(0);

        $this->registerClassPatch($alt1);
        $this->registerClassPatch($alt2);
        $this->registerClassPatch($alt3);
        $this->registerClassPatch($alt4);

        $this->getClassPatches()->shouldReturn(array($alt2, $alt3, $alt1, $alt4));
    }

    function its_double_mirrors_alterates_and_instantiates_provided_class(
        $mirror,
        $creator,
        $namer,
        ClassPatchInterface $alt1,
        ClassPatchInterface $alt2,
        \ReflectionClass $class,
        \ReflectionClass $interface1,
        \ReflectionClass $interface2,
        ClassNode $node
    ) {
        $mirror->reflect($class, array($interface1, $interface2))->willReturn($node);
        $alt1->supports($node)->willReturn(true);
        $alt2->supports($node)->willReturn(false);
        $alt1->getPriority()->willReturn(1);
        $alt2->getPriority()->willReturn(2);
        $namer->name($class, array($interface1, $interface2))->willReturn('SplStack');
        $class->getName()->willReturn('stdClass');
        $interface1->getName()->willReturn('ArrayAccess');
        $interface2->getName()->willReturn('Iterator');

        $alt1->apply($node)->shouldBeCalled();
        $alt2->apply($node)->shouldNotBeCalled();
        $creator->create('SplStack', $node)->shouldBeCalled();

        $this->registerClassPatch($alt1);
        $this->registerClassPatch($alt2);

        $this->double($class, array($interface1, $interface2))
            ->shouldReturnAnInstanceOf('SplStack');
    }

    function it_double_instantiates_a_class_with_constructor_argument(
        $mirror,
        \ReflectionClass $class,
        ClassNode $node,
        $namer
    ) {
        $class->getName()->willReturn('ReflectionClass');
        $mirror->reflect($class, array())->willReturn($node);
        $namer->name($class, array())->willReturn('ReflectionClass');

        $double = $this->double($class, array(), array('stdClass'));
        $double->shouldBeAnInstanceOf('ReflectionClass');
        $double->getName()->shouldReturn('stdClass');
    }

    function it_can_instantiate_class_with_final_constructor(
        $mirror,
        \ReflectionClass $class,
        ClassNode $node,
        $namer
    ) {
        $class->getName()->willReturn('spec\Prophecy\Doubler\WithFinalConstructor');
        $mirror->reflect($class, array())->willReturn($node);
        $namer->name($class, array())->willReturn('spec\Prophecy\Doubler\WithFinalConstructor');

        $double = $this->double($class, array());

        $double->shouldBeAnInstanceOf('spec\Prophecy\Doubler\WithFinalConstructor');
    }
}

class WithFinalConstructor
{
    final public function __construct() {}
}
<?php

namespace spec\Prophecy\Doubler\Generator;

use phpDocumentor\Reflection\DocBlock\Tags\Method;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Prophecy\Doubler\Generator\Node\ArgumentNode;
use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;

class ClassCodeGeneratorSpec extends ObjectBehavior
{
    function it_generates_proper_php_code_for_specific_ClassNode(
        ClassNode $class,
        MethodNode $method1,
        MethodNode $method2,
        MethodNode $method3,
        MethodNode $method4,
        ArgumentNode $argument11,
        ArgumentNode $argument12,
        ArgumentNode $argument21,
        ArgumentNode $argument31
    ) {
        $class->getParentClass()->willReturn('RuntimeException');
        $class->getInterfaces()->willReturn(array(
            'Prophecy\Doubler\Generator\MirroredInterface', 'ArrayAccess', 'ArrayIterator'
        ));
        $class->getProperties()->willReturn(array('name' => 'public', 'email' => 'private'));
        $class->getMethods()->willReturn(array($method1, $method2, $method3, $method4));

        $method1->getName()->willReturn('getName');
        $method1->getVisibility()->willReturn('public');
        $method1->returnsReference()->willReturn(false);
        $method1->isStatic()->willReturn(true);
        $method1->getArguments()->willReturn(array($argument11, $argument12));
        $method1->hasReturnType()->willReturn(true);
        $method1->getReturnType()->willReturn('string');
        $method1->hasNullableReturnType()->willReturn(true);
        $method1->getCode()->willReturn('return $this->name;');

        $method2->getName()->willReturn('getEmail');
        $method2->getVisibility()->willReturn('protected');
        $method2->returnsReference()->willReturn(false);
        $method2->isStatic()->willReturn(false);
        $method2->getArguments()->willReturn(array($argument21));
        $method2->hasReturnType()->willReturn(false);
        $method2->hasNullableReturnType()->willReturn(true);
        $method2->getCode()->willReturn('return $this->email;');

        $method3->getName()->willReturn('getRefValue');
        $method3->getVisibility()->willReturn('public');
        $method3->returnsReference()->willReturn(true);
        $method3->isStatic()->willReturn(false);
        $method3->getArguments()->willReturn(array($argument31));
        $method3->hasReturnType()->willReturn(true);
        $method3->getReturnType()->willReturn('string');
        $method3->hasNullableReturnType()->willReturn(false);
        $method3->getCode()->willReturn('return $this->refValue;');

        $method4->getName()->willReturn('doSomething');
        $method4->getVisibility()->willReturn('public');
        $method4->returnsReference()->willReturn(false);
        $method4->isStatic()->willReturn(false);
        $method4->getArguments()->willReturn(array());
        $method4->hasReturnType()->willReturn(true);
        $method4->getReturnType()->willReturn('void');
        $method4->hasNullableReturnType()->willReturn(false);
        $method4->getCode()->willReturn('return;');

        $argument11->getName()->willReturn('fullname');
        $argument11->getTypeHint()->willReturn('array');
        $argument11->isOptional()->willReturn(true);
        $argument11->getDefault()->willReturn(null);
        $argument11->isPassedByReference()->willReturn(false);
        $argument11->isVariadic()->willReturn(false);
        $argument11->isNullable()->willReturn(false);

        $argument12->getName()->willReturn('class');
        $argument12->getTypeHint()->willReturn('ReflectionClass');
        $argument12->isOptional()->willReturn(false);
        $argument12->isPassedByReference()->willReturn(false);
        $argument12->isVariadic()->willReturn(false);
        $argument12->isNullable()->willReturn(false);

        $argument21->getName()->willReturn('default');
        $argument21->getTypeHint()->willReturn('string');
        $argument21->isOptional()->willReturn(true);
        $argument21->getDefault()->willReturn('ever.zet@gmail.com');
        $argument21->isPassedByReference()->willReturn(false);
        $argument21->isVariadic()->willReturn(false);
        $argument21->isNullable()->willReturn(true);

        $argument31->getName()->willReturn('refValue');
        $argument31->getTypeHint()->willReturn(null);
        $argument31->isOptional()->willReturn(false);
        $argument31->getDefault()->willReturn();
        $argument31->isPassedByReference()->willReturn(false);
        $argument31->isVariadic()->willReturn(false);
        $argument31->isNullable()->willReturn(false);

        $code = $this->generate('CustomClass', $class);

        if (version_compare(PHP_VERSION, '7.1', '>=')) {
            $expected = <<<'PHP'
namespace  {
class CustomClass extends \RuntimeException implements \Prophecy\Doubler\Generator\MirroredInterface, \ArrayAccess, \ArrayIterator {
public $name;
private $email;

public static function getName(array $fullname = NULL, \ReflectionClass $class): ?string {
return $this->name;
}
protected  function getEmail(?string $default = 'ever.zet@gmail.com') {
return $this->email;
}
public  function &getRefValue( $refValue): string {
return $this->refValue;
}
public  function doSomething(): void {
return;
}

}
}
PHP;
        } elseif (version_compare(PHP_VERSION, '7.0', '>=')) {
            $expected = <<<'PHP'
namespace  {
class CustomClass extends \RuntimeException implements \Prophecy\Doubler\Generator\MirroredInterface, \ArrayAccess, \ArrayIterator {
public $name;
private $email;

public static function getName(array $fullname = NULL, \ReflectionClass $class): string {
return $this->name;
}
protected  function getEmail(string $default = 'ever.zet@gmail.com') {
return $this->email;
}
public  function &getRefValue( $refValue): string {
return $this->refValue;
}
public  function doSomething() {
return;
}

}
}
PHP;
        } else {
            $expected = <<<'PHP'
namespace  {
class CustomClass extends \RuntimeException implements \Prophecy\Doubler\Generator\MirroredInterface, \ArrayAccess, \ArrayIterator {
public $name;
private $email;

public static function getName(array $fullname = NULL, \ReflectionClass $class) {
return $this->name;
}
protected  function getEmail(\string $default = 'ever.zet@gmail.com') {
return $this->email;
}
public  function &getRefValue( $refValue) {
return $this->refValue;
}
public  function doSomething() {
return;
}

}
}
PHP;
        }
        $expected = strtr($expected, array("\r\n" => "\n", "\r" => "\n"));
        $code->shouldBe($expected);
    }

    function it_generates_proper_php_code_for_variadics(
        ClassNode $class,
        MethodNode $method1,
        MethodNode $method2,
        MethodNode $method3,
        MethodNode $method4,
        ArgumentNode $argument1,
        ArgumentNode $argument2,
        ArgumentNode $argument3,
        ArgumentNode $argument4
    ) {
        $class->getParentClass()->willReturn('stdClass');
        $class->getInterfaces()->willReturn(array('Prophecy\Doubler\Generator\MirroredInterface'));
        $class->getProperties()->willReturn(array());
        $class->getMethods()->willReturn(array(
            $method1, $method2, $method3, $method4
        ));

        $method1->getName()->willReturn('variadic');
        $method1->getVisibility()->willReturn('public');
        $method1->returnsReference()->willReturn(false);
        $method1->isStatic()->willReturn(false);
        $method1->getArguments()->willReturn(array($argument1));
        $method1->hasReturnType()->willReturn(false);
        $method1->getCode()->willReturn('');

        $method2->getName()->willReturn('variadicByRef');
        $method2->getVisibility()->willReturn('public');
        $method2->returnsReference()->willReturn(false);
        $method2->isStatic()->willReturn(false);
        $method2->getArguments()->willReturn(array($argument2));
        $method2->hasReturnType()->willReturn(false);
        $method2->getCode()->willReturn('');

        $method3->getName()->willReturn('variadicWithType');
        $method3->getVisibility()->willReturn('public');
        $method3->returnsReference()->willReturn(false);
        $method3->isStatic()->willReturn(false);
        $method3->getArguments()->willReturn(array($argument3));
        $method3->hasReturnType()->willReturn(false);
        $method3->getCode()->willReturn('');

        $method4->getName()->willReturn('variadicWithTypeByRef');
        $method4->getVisibility()->willReturn('public');
        $method4->returnsReference()->willReturn(false);
        $method4->isStatic()->willReturn(false);
        $method4->getArguments()->willReturn(array($argument4));
        $method4->hasReturnType()->willReturn(false);
        $method4->getCode()->willReturn('');

        $argument1->getName()->willReturn('args');
        $argument1->getTypeHint()->willReturn(null);
        $argument1->isOptional()->willReturn(false);
        $argument1->isPassedByReference()->willReturn(false);
        $argument1->isVariadic()->willReturn(true);
        $argument1->isNullable()->willReturn(false);

        $argument2->getName()->willReturn('args');
        $argument2->getTypeHint()->willReturn(null);
        $argument2->isOptional()->willReturn(false);
        $argument2->isPassedByReference()->willReturn(true);
        $argument2->isVariadic()->willReturn(true);
        $argument2->isNullable()->willReturn(false);

        $argument3->getName()->willReturn('args');
        $argument3->getTypeHint()->willReturn('\ReflectionClass');
        $argument3->isOptional()->willReturn(false);
        $argument3->isPassedByReference()->willReturn(false);
        $argument3->isVariadic()->willReturn(true);
        $argument3->isNullable()->willReturn(false);

        $argument4->getName()->willReturn('args');
        $argument4->getTypeHint()->willReturn('\ReflectionClass');
        $argument4->isOptional()->willReturn(false);
        $argument4->isPassedByReference()->willReturn(true);
        $argument4->isVariadic()->willReturn(true);
        $argument4->isNullable()->willReturn(false);

        $code = $this->generate('CustomClass', $class);
        $expected = <<<'PHP'
namespace  {
class CustomClass extends \stdClass implements \Prophecy\Doubler\Generator\MirroredInterface {

public  function variadic( ...$args) {

}
public  function variadicByRef( &...$args) {

}
public  function variadicWithType(\\ReflectionClass ...$args) {

}
public  function variadicWithTypeByRef(\\ReflectionClass &...$args) {

}

}
}
PHP;
        $expected = strtr($expected, array("\r\n" => "\n", "\r" => "\n"));
        $code->shouldBe($expected);
    }

    function it_overrides_properly_methods_with_args_passed_by_reference(
        ClassNode $class,
        MethodNode $method,
        ArgumentNode $argument
    ) {
        $class->getParentClass()->willReturn('RuntimeException');
        $class->getInterfaces()->willReturn(array('Prophecy\Doubler\Generator\MirroredInterface'));
        $class->getProperties()->willReturn(array());
        $class->getMethods()->willReturn(array($method));

        $method->getName()->willReturn('getName');
        $method->getVisibility()->willReturn('public');
        $method->isStatic()->willReturn(false);
        $method->getArguments()->willReturn(array($argument));
        $method->hasReturnType()->willReturn(false);
        $method->returnsReference()->willReturn(false);
        $method->getCode()->willReturn('return $this->name;');

        $argument->getName()->willReturn('fullname');
        $argument->getTypeHint()->willReturn('array');
        $argument->isOptional()->willReturn(true);
        $argument->getDefault()->willReturn(null);
        $argument->isPassedByReference()->willReturn(true);
        $argument->isVariadic()->willReturn(false);
        $argument->isNullable()->willReturn(false);

        $code = $this->generate('CustomClass', $class);
        $expected =<<<'PHP'
namespace  {
class CustomClass extends \RuntimeException implements \Prophecy\Doubler\Generator\MirroredInterface {

public  function getName(array &$fullname = NULL) {
return $this->name;
}

}
}
PHP;
        $expected = strtr($expected, array("\r\n" => "\n", "\r" => "\n"));
        $code->shouldBe($expected);
    }

    function it_generates_empty_class_for_empty_ClassNode(ClassNode $class)
    {
        $class->getParentClass()->willReturn('stdClass');
        $class->getInterfaces()->willReturn(array('Prophecy\Doubler\Generator\MirroredInterface'));
        $class->getProperties()->willReturn(array());
        $class->getMethods()->willReturn(array());

        $code = $this->generate('CustomClass', $class);
        $expected =<<<'PHP'
namespace  {
class CustomClass extends \stdClass implements \Prophecy\Doubler\Generator\MirroredInterface {


}
}
PHP;
        $expected = strtr($expected, array("\r\n" => "\n", "\r" => "\n"));
        $code->shouldBe($expected);
    }

    function it_wraps_class_in_namespace_if_it_is_namespaced(ClassNode $class)
    {
        $class->getParentClass()->willReturn('stdClass');
        $class->getInterfaces()->willReturn(array('Prophecy\Doubler\Generator\MirroredInterface'));
        $class->getProperties()->willReturn(array());
        $class->getMethods()->willReturn(array());

        $code = $this->generate('My\Awesome\CustomClass', $class);
        $expected =<<<'PHP'
namespace My\Awesome {
class CustomClass extends \stdClass implements \Prophecy\Doubler\Generator\MirroredInterface {


}
}
PHP;
        $expected = strtr($expected, array("\r\n" => "\n", "\r" => "\n"));
        $code->shouldBe($expected);
    }
}
<?php

namespace spec\Prophecy\Doubler\Generator;

use PhpSpec\ObjectBehavior;
use Prophecy\Doubler\Generator\ClassCodeGenerator;
use Prophecy\Doubler\Generator\Node\ClassNode;

class ClassCreatorSpec extends ObjectBehavior
{
    function let(ClassCodeGenerator $generator)
    {
        $this->beConstructedWith($generator);
    }

    function it_evaluates_code_generated_by_ClassCodeGenerator($generator, ClassNode $class)
    {
        $generator->generate('stdClass', $class)->shouldBeCalled()->willReturn(
            'return 42;'
        );

        $this->create('stdClass', $class)->shouldReturn(42);
    }

    function it_throws_an_exception_if_class_does_not_exist_after_evaluation($generator, ClassNode $class)
    {
        $generator->generate('CustomClass', $class)->shouldBeCalled()->willReturn(
            'return 42;'
        );

        $class->getParentClass()->willReturn('stdClass');
        $class->getInterfaces()->willReturn(array('Interface1', 'Interface2'));

        $this->shouldThrow('Prophecy\Exception\Doubler\ClassCreatorException')
            ->duringCreate('CustomClass', $class);
    }
}
<?php

namespace spec\Prophecy\Doubler\Generator\Node;

use PhpSpec\ObjectBehavior;

class ArgumentNodeSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith('name');
    }

    function it_is_not_be_passed_by_reference_by_default()
    {
        $this->shouldNotBePassedByReference();
    }

    function it_is_passed_by_reference_if_marked()
    {
        $this->setAsPassedByReference();
        $this->shouldBePassedByReference();
    }

    function it_is_not_variadic_by_default()
    {
        $this->shouldNotBeVariadic();
    }

    function it_is_variadic_if_marked()
    {
        $this->setAsVariadic();
        $this->shouldBeVariadic();
    }

    function it_does_not_have_default_by_default()
    {
        $this->shouldNotHaveDefault();
    }

    function it_does_not_have_default_if_variadic()
    {
        $this->setDefault(null);
        $this->setAsVariadic();
        $this->shouldNotHaveDefault();
    }

    function it_does_have_default_if_not_variadic()
    {
        $this->setDefault(null);
        $this->setAsVariadic(false);
        $this->hasDefault()->shouldReturn(true);
    }

    function it_has_name_with_which_it_was_been_constructed()
    {
        $this->getName()->shouldReturn('name');
    }

    function it_has_no_typehint_by_default()
    {
        $this->getTypeHint()->shouldReturn(null);
    }

    function its_typeHint_is_mutable()
    {
        $this->setTypeHint('array');
        $this->getTypeHint()->shouldReturn('array');
    }

    function it_does_not_have_default_value_by_default()
    {
        $this->getDefault()->shouldReturn(null);
    }

    function it_is_not_optional_by_default()
    {
        $this->isOptional()->shouldReturn(false);
    }

    function its_default_is_mutable()
    {
        $this->setDefault(array());
        $this->getDefault()->shouldReturn(array());
    }

    function it_is_marked_as_optional_when_default_is_set()
    {
        $this->setDefault(null);
        $this->isOptional()->shouldReturn(true);
    }
}
<?php

namespace spec\Prophecy\Doubler\Generator\Node;

use PhpSpec\ObjectBehavior;
use Prophecy\Doubler\Generator\Node\MethodNode;
use Prophecy\Exception\Doubler\MethodNotExtendableException;

class ClassNodeSpec extends ObjectBehavior
{
    function its_parentClass_is_a_stdClass_by_default()
    {
        $this->getParentClass()->shouldReturn('stdClass');
    }

    function its_parentClass_is_mutable()
    {
        $this->setParentClass('Exception');
        $this->getParentClass()->shouldReturn('Exception');
    }

    function its_parentClass_is_set_to_stdClass_if_user_set_null()
    {
        $this->setParentClass(null);
        $this->getParentClass()->shouldReturn('stdClass');
    }

    function it_does_not_implement_any_interface_by_default()
    {
        $this->getInterfaces()->shouldHaveCount(0);
    }

    function its_addInterface_adds_item_to_the_list_of_implemented_interfaces()
    {
        $this->addInterface('MyInterface');
        $this->getInterfaces()->shouldHaveCount(1);
    }

    function its_hasInterface_returns_true_if_class_implements_interface()
    {
        $this->addInterface('MyInterface');
        $this->hasInterface('MyInterface')->shouldReturn(true);
    }

    function its_hasInterface_returns_false_if_class_does_not_implements_interface()
    {
        $this->hasInterface('MyInterface')->shouldReturn(false);
    }

    function it_supports_implementation_of_multiple_interfaces()
    {
        $this->addInterface('MyInterface');
        $this->addInterface('MySecondInterface');
        $this->getInterfaces()->shouldHaveCount(2);
    }

    function it_ignores_same_interfaces_added_twice()
    {
        $this->addInterface('MyInterface');
        $this->addInterface('MyInterface');

        $this->getInterfaces()->shouldHaveCount(1);
        $this->getInterfaces()->shouldReturn(array('MyInterface'));
    }

    function it_does_not_have_methods_by_default()
    {
        $this->getMethods()->shouldHaveCount(0);
    }

    function it_can_has_methods(MethodNode $method1, MethodNode $method2)
    {
        $method1->getName()->willReturn('__construct');
        $method2->getName()->willReturn('getName');

        $this->addMethod($method1);
        $this->addMethod($method2);

        $this->getMethods()->shouldReturn(array(
            '__construct' => $method1,
            'getName'     => $method2
        ));
    }

    function its_hasMethod_returns_true_if_method_exists(MethodNode $method)
    {
        $method->getName()->willReturn('getName');

        $this->addMethod($method);

        $this->hasMethod('getName')->shouldReturn(true);
    }

    function its_getMethod_returns_method_by_name(MethodNode $method)
    {
        $method->getName()->willReturn('getName');

        $this->addMethod($method);

        $this->getMethod('getName')->shouldReturn($method);
    }

    function its_hasMethod_returns_false_if_method_does_not_exists()
    {
        $this->hasMethod('getName')->shouldReturn(false);
    }

    function its_hasMethod_returns_false_if_method_has_been_removed(MethodNode $method)
    {
        $method->getName()->willReturn('getName');
        $this->addMethod($method);
        $this->removeMethod('getName');

        $this->hasMethod('getName')->shouldReturn(false);
    }


    function it_does_not_have_properties_by_default()
    {
        $this->getProperties()->shouldHaveCount(0);
    }

    function it_is_able_to_have_properties()
    {
        $this->addProperty('title');
        $this->addProperty('text', 'private');
        $this->getProperties()->shouldReturn(array(
            'title' => 'public',
            'text'  => 'private'
        ));
    }

    function its_addProperty_does_not_accept_unsupported_visibility()
    {
        $this->shouldThrow('InvalidArgumentException')->duringAddProperty('title', 'town');
    }

    function its_addProperty_lowercases_visibility_before_setting()
    {
        $this->addProperty('text', 'PRIVATE');
        $this->getProperties()->shouldReturn(array('text' => 'private'));
    }

    function its_has_no_unextendable_methods_by_default()
    {
        $this->getUnextendableMethods()->shouldHaveCount(0);
    }

    function its_addUnextendableMethods_adds_an_unextendable_method()
    {
        $this->addUnextendableMethod('testMethod');
        $this->getUnextendableMethods()->shouldHaveCount(1);
    }

    function its_methods_are_extendable_by_default()
    {
        $this->isExtendable('testMethod')->shouldReturn(true);
    }

    function its_unextendable_methods_are_not_extendable()
    {
        $this->addUnextendableMethod('testMethod');
        $this->isExtendable('testMethod')->shouldReturn(false);
    }

    function its_addUnextendableMethods_doesnt_create_duplicates()
    {
        $this->addUnextendableMethod('testMethod');
        $this->addUnextendableMethod('testMethod');
        $this->getUnextendableMethods()->shouldHaveCount(1);
    }

    function it_throws_an_exception_when_adding_a_method_that_isnt_extendable(MethodNode $method)
    {
        $this->addUnextendableMethod('testMethod');
        $method->getName()->willReturn('testMethod');

        $expectedException = new MethodNotExtendableException(
            "Method `testMethod` is not extendable, so can not be added.",
            "stdClass",
            "testMethod"
        );
        $this->shouldThrow($expectedException)->duringAddMethod($method);
    }
}
<?php

namespace spec\Prophecy\Doubler\Generator\Node;

use PhpSpec\ObjectBehavior;
use Prophecy\Doubler\Generator\Node\ArgumentNode;

class MethodNodeSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith('getTitle');
    }

    function it_has_a_name()
    {
        $this->getName()->shouldReturn('getTitle');
    }

    function it_has_public_visibility_by_default()
    {
        $this->getVisibility()->shouldReturn('public');
    }

    function its_visibility_is_mutable()
    {
        $this->setVisibility('private');
        $this->getVisibility()->shouldReturn('private');
    }

    function it_is_not_static_by_default()
    {
        $this->shouldNotBeStatic();
    }

    function it_does_not_return_a_reference_by_default()
    {
        $this->returnsReference()->shouldReturn(false);
    }

    function it_should_be_settable_as_returning_a_reference_through_setter()
    {
        $this->setReturnsReference();
        $this->returnsReference()->shouldReturn(true);
    } 

    function it_should_be_settable_as_static_through_setter()
    {
        $this->setStatic();
        $this->shouldBeStatic();
    }

    function it_accepts_only_supported_visibilities()
    {
        $this->shouldThrow('InvalidArgumentException')->duringSetVisibility('stealth');
    }

    function it_lowercases_visibility_before_setting_it()
    {
        $this->setVisibility('Public');
        $this->getVisibility()->shouldReturn('public');
    }

    function its_useParentCode_causes_method_to_call_parent(ArgumentNode $argument1, ArgumentNode $argument2)
    {
        $argument1->getName()->willReturn('objectName');
        $argument2->getName()->willReturn('default');

        $argument1->isVariadic()->willReturn(false);
        $argument2->isVariadic()->willReturn(true);

        $this->addArgument($argument1);
        $this->addArgument($argument2);

        $this->useParentCode();

        $this->getCode()->shouldReturn(
            'return parent::getTitle($objectName, ...$default);'
        );
    }

    function its_code_is_mutable()
    {
        $this->setCode('echo "code";');
        $this->getCode()->shouldReturn('echo "code";');
    }

    function its_reference_returning_methods_will_generate_exceptions()
    {
        $this->setCode('echo "code";');
        $this->setReturnsReference();
        $this->getCode()->shouldReturn("throw new \Prophecy\Exception\Doubler\ReturnByReferenceException('Returning by reference not supported', get_class(\$this), 'getTitle');");
    }

    function its_setCode_provided_with_null_cleans_method_body()
    {
        $this->setCode(null);
        $this->getCode()->shouldReturn('');
    }

    function it_is_constructable_with_code()
    {
        $this->beConstructedWith('getTitle', 'die();');
        $this->getCode()->shouldReturn('die();');
    }

    function it_does_not_have_arguments_by_default()
    {
        $this->getArguments()->shouldHaveCount(0);
    }

    function it_supports_adding_arguments(ArgumentNode $argument1, ArgumentNode $argument2)
    {
        $this->addArgument($argument1);
        $this->addArgument($argument2);

        $this->getArguments()->shouldReturn(array($argument1, $argument2));
    }

    function it_does_not_have_return_type_by_default()
    {
        $this->hasReturnType()->shouldReturn(false);
    }

    function it_setReturnType_sets_return_type()
    {
        $returnType = 'string';

        $this->setReturnType($returnType);

        $this->hasReturnType()->shouldReturn(true);
        $this->getReturnType()->shouldReturn($returnType);
    }
}
<?php

namespace spec\Prophecy\Doubler;

use PhpSpec\ObjectBehavior;
use Prophecy\Doubler\Doubler;
use Prophecy\Prophecy\ProphecySubjectInterface;

class LazyDoubleSpec extends ObjectBehavior
{
    function let(Doubler $doubler)
    {
        $this->beConstructedWith($doubler);
    }

    function it_returns_anonymous_double_instance_by_default($doubler, ProphecySubjectInterface $double)
    {
        $doubler->double(null, array())->willReturn($double);

        $this->getInstance()->shouldReturn($double);
    }

    function it_returns_class_double_instance_if_set($doubler, ProphecySubjectInterface $double, \ReflectionClass $class)
    {
        $doubler->double($class, array())->willReturn($double);

        $this->setParentClass($class);

        $this->getInstance()->shouldReturn($double);
    }

    function it_returns_same_double_instance_if_called_2_times(
        $doubler,
        ProphecySubjectInterface $double1,
        ProphecySubjectInterface $double2
    ) {
        $doubler->double(null, array())->willReturn($double1);
        $doubler->double(null, array())->willReturn($double2);

        $this->getInstance()->shouldReturn($double2);
        $this->getInstance()->shouldReturn($double2);
    }

    function its_setParentClass_throws_ClassNotFoundException_if_class_not_found()
    {
        $this->shouldThrow('Prophecy\Exception\Doubler\ClassNotFoundException')
            ->duringSetParentClass('SomeUnexistingClass');
    }

    function its_setParentClass_throws_exception_if_prophecy_is_already_created(
        $doubler,
        ProphecySubjectInterface $double
    ) {
        $doubler->double(null, array())->willReturn($double);

        $this->getInstance();

        $this->shouldThrow('Prophecy\Exception\Doubler\DoubleException')
            ->duringSetParentClass('stdClass');
    }

    function its_addInterface_throws_InterfaceNotFoundException_if_no_interface_found()
    {
        $this->shouldThrow('Prophecy\Exception\Doubler\InterfaceNotFoundException')
            ->duringAddInterface('SomeUnexistingInterface');
    }

    function its_addInterface_throws_exception_if_prophecy_is_already_created(
        $doubler,
        ProphecySubjectInterface $double
    ) {
        $doubler->double(null, array())->willReturn($double);

        $this->getInstance();

        $this->shouldThrow('Prophecy\Exception\Doubler\DoubleException')
            ->duringAddInterface('ArrayAccess');
    }
}
<?php

namespace spec\Prophecy\Doubler;

use PhpSpec\ObjectBehavior;

class NameGeneratorSpec extends ObjectBehavior
{
    function its_name_generates_name_based_on_simple_class_reflection(\ReflectionClass $class)
    {
        $class->getName()->willReturn('stdClass');
        $this->name($class, array())->shouldStartWith('Double\stdClass\\');
    }

    function its_name_generates_name_based_on_namespaced_class_reflection(\ReflectionClass $class)
    {
        $class->getName()->willReturn('Some\Custom\Class');
        $this->name($class, array())->shouldStartWith('Double\Some\Custom\Class\P');
    }

    function its_name_generates_name_based_on_interface_shortnames(
        \ReflectionClass $interface1,
        \ReflectionClass $interface2
    ) {
        $interface1->getShortName()->willReturn('HandlerInterface');
        $interface2->getShortName()->willReturn('LoaderInterface');

        $this->name(null, array($interface1, $interface2))->shouldStartWith(
            'Double\HandlerInterface\LoaderInterface\P'
        );
    }

    function it_generates_proper_name_for_no_class_and_interfaces_list()
    {
        $this->name(null, array())->shouldStartWith('Double\stdClass\P');
    }

    function its_name_generates_name_based_only_on_class_if_its_available(
        \ReflectionClass $class,
        \ReflectionClass $interface1,
        \ReflectionClass $interface2
    ) {
        $class->getName()->willReturn('Some\Custom\Class');
        $interface1->getShortName()->willReturn('HandlerInterface');
        $interface2->getShortName()->willReturn('LoaderInterface');

        $this->name($class, array($interface1, $interface2))->shouldStartWith(
            'Double\Some\Custom\Class\P'
        );
    }

    public function getMatchers()
    {
        return array(
            'startWith' => function ($subject, $string) {
                return 0 === strpos($subject, $string);
            },
        );
    }
}
<?php

namespace spec\Prophecy\Exception\Call;

use PhpSpec\ObjectBehavior;
use Prophecy\Prophecy\ObjectProphecy;
use spec\Prophecy\Exception\Prophecy\Prophecy;

class UnexpectedCallExceptionSpec extends ObjectBehavior
{
    function let(ObjectProphecy $objectProphecy)
    {
        $this->beConstructedWith('msg', $objectProphecy, 'getName', array('arg1', 'arg2'));
    }

    function it_is_prophecy_exception()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Exception\Prophecy\ObjectProphecyException');
    }

    function it_exposes_method_name_through_getter()
    {
        $this->getMethodName()->shouldReturn('getName');
    }

    function it_exposes_arguments_through_getter()
    {
        $this->getArguments()->shouldReturn(array('arg1', 'arg2'));
    }
}
<?php

namespace spec\Prophecy\Exception\Doubler;

use PhpSpec\ObjectBehavior;
use Prophecy\Doubler\Generator\Node\ClassNode;
use spec\Prophecy\Exception\Prophecy;

class ClassCreatorExceptionSpec extends ObjectBehavior
{
    function let(ClassNode $node)
    {
        $this->beConstructedWith('', $node);
    }

    function it_is_a_prophecy_exception()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Exception\Exception');
        $this->shouldBeAnInstanceOf('Prophecy\Exception\Doubler\DoublerException');
    }

    function it_contains_a_reflected_node($node)
    {
        $this->getClassNode()->shouldReturn($node);
    }
}
<?php

namespace spec\Prophecy\Exception\Doubler;

use PhpSpec\ObjectBehavior;

class ClassMirrorExceptionSpec extends ObjectBehavior
{
    function let(\ReflectionClass $class)
    {
        $this->beConstructedWith('', $class);
    }

    function it_is_a_prophecy_exception()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Exception\Exception');
        $this->shouldBeAnInstanceOf('Prophecy\Exception\Doubler\DoublerException');
    }

    function it_contains_a_reflected_class_link($class)
    {
        $this->getReflectedClass()->shouldReturn($class);
    }
}
<?php

namespace spec\Prophecy\Exception\Doubler;

use PhpSpec\ObjectBehavior;
use spec\Prophecy\Exception\Prophecy;

class ClassNotFoundExceptionSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith('msg', 'CustomClass');
    }

    function it_is_a_prophecy_exception()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Exception\Exception');
        $this->shouldBeAnInstanceOf('Prophecy\Exception\Doubler\DoubleException');
    }

    function its_getClassname_returns_classname()
    {
        $this->getClassname()->shouldReturn('CustomClass');
    }
}
<?php

namespace spec\Prophecy\Exception\Doubler;

use PhpSpec\ObjectBehavior;

class DoubleExceptionSpec extends ObjectBehavior
{
    function it_is_a_double_exception()
    {
        $this->shouldBeAnInstanceOf('RuntimeException');
        $this->shouldBeAnInstanceOf('Prophecy\Exception\Doubler\DoublerException');
    }
}
<?php

namespace spec\Prophecy\Exception\Doubler;

use PhpSpec\ObjectBehavior;
use spec\Prophecy\Exception\Prophecy;

class InterfaceNotFoundExceptionSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith('msg', 'CustomInterface');
    }

    function it_extends_ClassNotFoundException()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Exception\Doubler\ClassNotFoundException');
    }

    function its_getClassname_returns_classname()
    {
        $this->getClassname()->shouldReturn('CustomInterface');
    }
}
<?php

namespace spec\Prophecy\Exception\Doubler;

use PhpSpec\ObjectBehavior;
use spec\Prophecy\Exception\Prophecy;

class MethodNotExtendableExceptionSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith('', 'User', 'getName');
    }

    function it_is_DoubleException()
    {
        $this->shouldHaveType('Prophecy\Exception\Doubler\DoubleException');
    }

    function it_has_MethodName()
    {
        $this->getMethodName()->shouldReturn('getName');
    }

    function it_has_classname()
    {
        $this->getClassName()->shouldReturn('User');
    }
}
<?php

namespace spec\Prophecy\Exception\Doubler;

use PhpSpec\ObjectBehavior;
use spec\Prophecy\Exception\Prophecy;

class MethodNotFoundExceptionSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith('', 'User', 'getName', array(1, 2, 3));
    }

    function it_is_DoubleException()
    {
        $this->shouldHaveType('Prophecy\Exception\Doubler\DoubleException');
    }

    function it_has_MethodName()
    {
        $this->getMethodName()->shouldReturn('getName');
    }

    function it_has_classnamej()
    {
        $this->getClassname()->shouldReturn('User');
    }

    function it_has_an_arguments_list()
    {
        $this->getArguments()->shouldReturn(array(1, 2, 3));
    }

    function it_has_a_default_null_argument_list()
    {
        $this->beConstructedWith('', 'User', 'getName');
        $this->getArguments()->shouldReturn(null);
    }
}
<?php

namespace spec\Prophecy\Exception\Prediction;

use PhpSpec\ObjectBehavior;
use Prophecy\Exception\Prediction\PredictionException;
use Prophecy\Prophecy\ObjectProphecy;

class AggregateExceptionSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith(null);
    }

    function it_is_prediction_exception()
    {
        $this->shouldBeAnInstanceOf('RuntimeException');
        $this->shouldBeAnInstanceOf('Prophecy\Exception\Prediction\PredictionException');
    }

    function it_can_store_objectProphecy_link(ObjectProphecy $object)
    {
        $this->setObjectProphecy($object);
        $this->getObjectProphecy()->shouldReturn($object);
    }

    function it_should_not_have_exceptions_at_the_beginning()
    {
        $this->getExceptions()->shouldHaveCount(0);
    }

    function it_should_append_exception_through_append_method(PredictionException $exception)
    {
        $exception->getMessage()->willReturn('Exception #1');

        $this->append($exception);

        $this->getExceptions()->shouldReturn(array($exception));
    }

    function it_should_update_message_during_append(PredictionException $exception)
    {
        $exception->getMessage()->willReturn('Exception #1');

        $this->append($exception);

        $this->getMessage()->shouldReturn("  Exception #1");
    }
}
<?php

namespace spec\Prophecy\Exception\Prediction;

use PhpSpec\ObjectBehavior;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;

class NoCallsExceptionSpec extends ObjectBehavior
{
    function let(ObjectProphecy $objectProphecy, MethodProphecy $methodProphecy)
    {
        $methodProphecy->getObjectProphecy()->willReturn($objectProphecy);

        $this->beConstructedWith('message', $methodProphecy);
    }

    function it_is_PredictionException()
    {
        $this->shouldHaveType('Prophecy\Exception\Prediction\PredictionException');
    }

    function it_extends_MethodProphecyException()
    {
        $this->shouldHaveType('Prophecy\Exception\Prophecy\MethodProphecyException');
    }
}
<?php

namespace spec\Prophecy\Exception\Prediction;

use PhpSpec\ObjectBehavior;
use Prophecy\Call\Call;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;

class UnexpectedCallsCountExceptionSpec extends ObjectBehavior
{
    function let( ObjectProphecy $objectProphecy, MethodProphecy $methodProphecy, Call $call1, Call $call2) {
        $methodProphecy->getObjectProphecy()->willReturn($objectProphecy);

        $this->beConstructedWith('message', $methodProphecy, 5, array($call1, $call2));
    }

    function it_extends_UnexpectedCallsException()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Exception\Prediction\UnexpectedCallsException');
    }

    function it_should_expose_expectedCount_through_getter()
    {
        $this->getExpectedCount()->shouldReturn(5);
    }
}
<?php

namespace spec\Prophecy\Exception\Prediction;

use PhpSpec\ObjectBehavior;
use Prophecy\Call\Call;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;

class UnexpectedCallsExceptionSpec extends ObjectBehavior
{
    function let(ObjectProphecy $objectProphecy, MethodProphecy $methodProphecy, Call $call1, Call $call2)
    {
        $methodProphecy->getObjectProphecy()->willReturn($objectProphecy);

        $this->beConstructedWith('message', $methodProphecy, array($call1, $call2));
    }

    function it_is_PredictionException()
    {
        $this->shouldHaveType('Prophecy\Exception\Prediction\PredictionException');
    }

    function it_extends_MethodProphecyException()
    {
        $this->shouldHaveType('Prophecy\Exception\Prophecy\MethodProphecyException');
    }

    function it_should_expose_calls_list_through_getter($call1, $call2)
    {
        $this->getCalls()->shouldReturn(array($call1, $call2));
    }
}
<?php

namespace spec\Prophecy\Exception\Prophecy;

use PhpSpec\ObjectBehavior;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use spec\Prophecy\Exception\Prophecy;

class MethodProphecyExceptionSpec extends ObjectBehavior
{
    function let(ObjectProphecy $objectProphecy, MethodProphecy $methodProphecy)
    {
        $methodProphecy->getObjectProphecy()->willReturn($objectProphecy);

        $this->beConstructedWith('message', $methodProphecy);
    }

    function it_extends_DoubleException()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Exception\Prophecy\ObjectProphecyException');
    }

    function it_holds_a_stub_reference($methodProphecy)
    {
        $this->getMethodProphecy()->shouldReturn($methodProphecy);
    }
}
<?php

namespace spec\Prophecy\Exception\Prophecy;

use PhpSpec\ObjectBehavior;
use Prophecy\Prophecy\ObjectProphecy;

class ObjectProphecyExceptionSpec extends ObjectBehavior
{
    function let(ObjectProphecy $objectProphecy)
    {
        $this->beConstructedWith('message', $objectProphecy);
    }

    function it_should_be_a_prophecy_exception()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Exception\Prophecy\ProphecyException');
    }

    function it_holds_double_reference($objectProphecy)
    {
        $this->getObjectProphecy()->shouldReturn($objectProphecy);
    }
}
<?php

namespace spec\Prophecy\Prediction;

use PhpSpec\ObjectBehavior;

use Prophecy\Call\Call;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use RuntimeException;

class CallbackPredictionSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith('get_class');
    }

    function it_is_prediction()
    {
        $this->shouldHaveType('Prophecy\Prediction\PredictionInterface');
    }

    function it_proxies_call_to_callback(ObjectProphecy $object, MethodProphecy $method, Call $call)
    {
        $returnFirstCallCallback = function ($calls, $object, $method) {
            throw new RuntimeException;
        };

        $this->beConstructedWith($returnFirstCallCallback);

        $this->shouldThrow('RuntimeException')->duringCheck(array($call), $object, $method);
    }
}
<?php

namespace spec\Prophecy\Prediction;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Prophecy\Argument\ArgumentsWildcard;
use Prophecy\Call\Call;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;

class CallPredictionSpec extends ObjectBehavior
{
    function it_is_prediction()
    {
        $this->shouldHaveType('Prophecy\Prediction\PredictionInterface');
    }

    function it_does_nothing_if_there_is_more_than_one_call_been_made(
        ObjectProphecy $object,
        MethodProphecy $method,
        Call $call
    ) {
        $this->check(array($call), $object, $method)->shouldReturn(null);
    }

    function it_throws_NoCallsException_if_no_calls_found(
        ObjectProphecy $object,
        MethodProphecy $method,
        ArgumentsWildcard $arguments
    ) {
        $method->getObjectProphecy()->willReturn($object);
        $method->getMethodName()->willReturn('getName');
        $method->getArgumentsWildcard()->willReturn($arguments);
        $arguments->__toString()->willReturn('123');
        $object->reveal()->willReturn(new \stdClass());
        $object->findProphecyMethodCalls('getName', Argument::any())->willReturn(array());

        $this->shouldThrow('Prophecy\Exception\Prediction\NoCallsException')
            ->duringCheck(array(), $object, $method);
    }
}
<?php

namespace spec\Prophecy\Prediction;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument\ArgumentsWildcard;
use Prophecy\Call\Call;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;

class CallTimesPredictionSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith(2);
    }

    function it_is_prediction()
    {
        $this->shouldHaveType('Prophecy\Prediction\PredictionInterface');
    }

    function it_does_nothing_if_there_were_exact_amount_of_calls_being_made(
        ObjectProphecy $object,
        MethodProphecy $method,
        Call $call1,
        Call $call2
    ) {
        $this->check(array($call1, $call2), $object, $method)->shouldReturn(null);
    }

    function it_throws_UnexpectedCallsCountException_if_calls_found(
        ObjectProphecy $object,
        MethodProphecy $method,
        Call $call,
        ArgumentsWildcard $arguments
    ) {
        $method->getObjectProphecy()->willReturn($object);
        $method->getMethodName()->willReturn('getName');
        $method->getArgumentsWildcard()->willReturn($arguments);
        $arguments->__toString()->willReturn('123');

        $call->getMethodName()->willReturn('getName');
        $call->getArguments()->willReturn(array(5, 4, 'three'));
        $call->getCallPlace()->willReturn('unknown');

        $this->shouldThrow('Prophecy\Exception\Prediction\UnexpectedCallsCountException')
            ->duringCheck(array($call), $object, $method);
    }
}
<?php

namespace spec\Prophecy\Prediction;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument\ArgumentsWildcard;
use Prophecy\Call\Call;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;

class NoCallsPredictionSpec extends ObjectBehavior
{
    function it_is_prediction()
    {
        $this->shouldHaveType('Prophecy\Prediction\PredictionInterface');
    }

    function it_does_nothing_if_there_is_no_calls_made(ObjectProphecy $object, MethodProphecy $method)
    {
        $this->check(array(), $object, $method)->shouldReturn(null);
    }

    function it_throws_UnexpectedCallsException_if_calls_found(
        ObjectProphecy $object,
        MethodProphecy $method,
        Call $call,
        ArgumentsWildcard $arguments
    ) {
        $method->getObjectProphecy()->willReturn($object);
        $method->getMethodName()->willReturn('getName');
        $method->getArgumentsWildcard()->willReturn($arguments);
        $arguments->__toString()->willReturn('123');

        $call->getMethodName()->willReturn('getName');
        $call->getArguments()->willReturn(array(5, 4, 'three'));
        $call->getCallPlace()->willReturn('unknown');

        $this->shouldThrow('Prophecy\Exception\Prediction\UnexpectedCallsException')
            ->duringCheck(array($call), $object, $method);
    }
}
<?php

namespace spec\Prophecy\Promise;

use PhpSpec\ObjectBehavior;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;

class CallbackPromiseSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith('get_class');
    }

    function it_is_promise()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Promise\PromiseInterface');
    }

    function it_should_execute_closure_callback(ObjectProphecy $object, MethodProphecy $method)
    {
        $firstArgumentCallback = function ($args) {
            return $args[0];
        };

        $this->beConstructedWith($firstArgumentCallback);

        $this->execute(array('one', 'two'), $object, $method)->shouldReturn('one');
    }

    function it_should_execute_static_array_callback(ObjectProphecy $object, MethodProphecy $method)
    {
        $firstArgumentCallback = array('spec\Prophecy\Promise\ClassCallback', 'staticCallbackMethod');

        $this->beConstructedWith($firstArgumentCallback);

        $this->execute(array('one', 'two'), $object, $method)->shouldReturn('one');
    }

    function it_should_execute_instance_array_callback(ObjectProphecy $object, MethodProphecy $method)
    {
        $class = new ClassCallback();
        $firstArgumentCallback = array($class, 'callbackMethod');

        $this->beConstructedWith($firstArgumentCallback);

        $this->execute(array('one', 'two'), $object, $method)->shouldReturn('one');
    }

    function it_should_execute_string_function_callback(ObjectProphecy $object, MethodProphecy $method)
    {
        $firstArgumentCallback = 'spec\Prophecy\Promise\functionCallbackFirstArgument';

        $this->beConstructedWith($firstArgumentCallback);

        $this->execute(array('one', 'two'), $object, $method)->shouldReturn('one');
    }

}

/**
 * Class used to test callbackpromise
 *
 * @param array
 * @return string
 */
class ClassCallback
{
    /**
     * @param array $args
     */
    function callbackMethod($args)
    {
        return $args[0];
    }

    /**
     * @param array $args
     */
    static function staticCallbackMethod($args)
    {
        return $args[0];
    }
}

/**
 * Callback function used to test callbackpromise
 *
 * @param array
 * @return string
 */
function functionCallbackFirstArgument($args)
{
    return $args[0];
}
<?php

namespace spec\Prophecy\Promise;

use PhpSpec\ObjectBehavior;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;

class ReturnArgumentPromiseSpec extends ObjectBehavior
{
    function it_is_promise()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Promise\PromiseInterface');
    }

    function it_should_return_first_argument_if_provided(ObjectProphecy $object, MethodProphecy $method)
    {
        $this->execute(array('one', 'two'), $object, $method)->shouldReturn('one');
    }

    function it_should_return_null_if_no_arguments_provided(ObjectProphecy $object, MethodProphecy $method)
    {
        $this->execute(array(), $object, $method)->shouldReturn(null);
    }

    function it_should_return_nth_argument_if_provided(ObjectProphecy $object, MethodProphecy $method)
    {
        $this->beConstructedWith(1);
        $this->execute(array('one', 'two'), $object, $method)->shouldReturn('two');
    }
}
<?php

namespace spec\Prophecy\Promise;

use PhpSpec\ObjectBehavior;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;

class ReturnPromiseSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith(array(42));
    }

    function it_is_promise()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Promise\PromiseInterface');
    }

    function it_returns_value_it_was_constructed_with(ObjectProphecy $object, MethodProphecy $method)
    {
        $this->execute(array(), $object, $method)->shouldReturn(42);
    }

    function it_always_returns_last_value_left_in_the_return_values(ObjectProphecy $object, MethodProphecy $method)
    {
        $this->execute(array(), $object, $method)->shouldReturn(42);
        $this->execute(array(), $object, $method)->shouldReturn(42);
    }

    function it_consequently_returns_multiple_values_it_was_constructed_with(
        ObjectProphecy $object,
        MethodProphecy $method
    ) {
        $this->beConstructedWith(array(42, 24, 12));

        $this->execute(array(), $object, $method)->shouldReturn(42);
        $this->execute(array(), $object, $method)->shouldReturn(24);
        $this->execute(array(), $object, $method)->shouldReturn(12);
    }

    function it_returns_null_if_constructed_with_empty_array(ObjectProphecy $object, MethodProphecy $method)
    {
        $this->beConstructedWith(array());

        $this->execute(array(), $object, $method)->shouldReturn(null);
    }
}
<?php

namespace spec\Prophecy\Promise;

use PhpSpec\Exception\Example\SkippingException;
use PhpSpec\ObjectBehavior;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;

class ThrowPromiseSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith('RuntimeException');
    }

    function it_is_promise()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Promise\PromiseInterface');
    }

    function it_instantiates_and_throws_exception_from_provided_classname(ObjectProphecy $object, MethodProphecy $method)
    {
        $this->beConstructedWith('InvalidArgumentException');

        $this->shouldThrow('InvalidArgumentException')
            ->duringExecute(array(), $object, $method);
    }

    function it_instantiates_exceptions_with_required_arguments(ObjectProphecy $object, MethodProphecy $method)
    {
        $this->beConstructedWith('spec\Prophecy\Promise\RequiredArgumentException');

        $this->shouldThrow('spec\Prophecy\Promise\RequiredArgumentException')
            ->duringExecute(array(), $object, $method);
    }

    function it_throws_provided_exception(ObjectProphecy $object, MethodProphecy $method)
    {
        $this->beConstructedWith($exc = new \RuntimeException('Some exception'));

        $this->shouldThrow($exc)->duringExecute(array(), $object, $method);
    }

    function it_throws_error_instances(ObjectProphecy $object, MethodProphecy $method)
    {
        if (!class_exists('\Error')) {
            throw new SkippingException('The class Error, introduced in PHP 7, does not exist');
        }

        $this->beConstructedWith($exc = new \Error('Error exception'));

        $this->shouldThrow($exc)->duringExecute(array(), $object, $method);
    }

    function it_throws_errors_by_class_name()
    {
        if (!class_exists('\Error')) {
            throw new SkippingException('The class Error, introduced in PHP 7, does not exist');
        }

        $this->beConstructedWith('\Error');

        $this->shouldNotThrow('Prophecy\Exception\InvalidArgumentException')->duringInstantiation();
    }

    function it_does_not_throw_something_that_is_not_throwable_by_class_name()
    {
        $this->beConstructedWith('\stdClass');

        $this->shouldThrow('Prophecy\Exception\InvalidArgumentException')->duringInstantiation();
    }

    function it_does_not_throw_something_that_is_not_throwable_by_instance()
    {
        $this->beConstructedWith(new \stdClass());

        $this->shouldThrow('Prophecy\Exception\InvalidArgumentException')->duringInstantiation();
    }

    function it_throws_an_exception_by_class_name()
    {
        $this->beConstructedWith('\Exception');

        $this->shouldNotThrow('Prophecy\Exception\InvalidArgumentException')->duringInstantiation();
    }
}

class RequiredArgumentException extends \Exception
{
    final public function __construct($message, $code) {}
}
<?php

namespace spec\Prophecy\Prophecy;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument\ArgumentsWildcard;
use Prophecy\Call\Call;
use Prophecy\Prediction\PredictionInterface;
use Prophecy\Promise\PromiseInterface;
use Prophecy\Prophecy\ObjectProphecy;

class ClassWithFinalMethod
{
    final public function finalMethod() {}
}

class MethodProphecySpec extends ObjectBehavior
{
    function let(ObjectProphecy $objectProphecy, \ReflectionClass $reflection)
    {
        $objectProphecy->reveal()->willReturn($reflection);

        $this->beConstructedWith($objectProphecy, 'getName', null);
    }

    function it_is_initializable()
    {
        $this->shouldHaveType('Prophecy\Prophecy\MethodProphecy');
    }

    function its_constructor_throws_MethodNotFoundException_for_unexisting_method($objectProphecy)
    {
        $this->shouldThrow('Prophecy\Exception\Doubler\MethodNotFoundException')->during(
            '__construct', array($objectProphecy, 'getUnexisting', null)
        );
    }

    function its_constructor_throws_MethodProphecyException_for_final_methods($objectProphecy, ClassWithFinalMethod $subject)
    {
        $objectProphecy->reveal()->willReturn($subject);

        $this->shouldThrow('Prophecy\Exception\Prophecy\MethodProphecyException')->during(
            '__construct', array($objectProphecy, 'finalMethod', null)
        );
    }

    function its_constructor_transforms_array_passed_as_3rd_argument_to_ArgumentsWildcard(
        $objectProphecy
    )
    {
        $this->beConstructedWith($objectProphecy, 'getName', array(42, 33));

        $wildcard = $this->getArgumentsWildcard();
        $wildcard->shouldNotBe(null);
        $wildcard->__toString()->shouldReturn('exact(42), exact(33)');
    }

    function its_constructor_does_not_touch_third_argument_if_it_is_null($objectProphecy)
    {
        $this->beConstructedWith($objectProphecy, 'getName', null);

        $wildcard = $this->getArgumentsWildcard();
        $wildcard->shouldBe(null);
    }

    function it_records_promise_through_will_method(PromiseInterface $promise, $objectProphecy)
    {
        $objectProphecy->addMethodProphecy($this)->willReturn(null);

        $this->will($promise);
        $this->getPromise()->shouldReturn($promise);
    }

    function it_adds_itself_to_ObjectProphecy_during_call_to_will(PromiseInterface $objectProphecy, $promise)
    {
        $objectProphecy->addMethodProphecy($this)->shouldBeCalled();

        $this->will($promise);
    }

    function it_adds_ReturnPromise_during_willReturn_call($objectProphecy)
    {
        $objectProphecy->addMethodProphecy($this)->willReturn(null);

        $this->willReturn(42);
        $this->getPromise()->shouldBeAnInstanceOf('Prophecy\Promise\ReturnPromise');
    }

    function it_adds_ThrowPromise_during_willThrow_call($objectProphecy)
    {
        $objectProphecy->addMethodProphecy($this)->willReturn(null);

        $this->willThrow('RuntimeException');
        $this->getPromise()->shouldBeAnInstanceOf('Prophecy\Promise\ThrowPromise');
    }

    function it_adds_ReturnArgumentPromise_during_willReturnArgument_call($objectProphecy)
    {
        $objectProphecy->addMethodProphecy($this)->willReturn(null);

        $this->willReturnArgument();
        $this->getPromise()->shouldBeAnInstanceOf('Prophecy\Promise\ReturnArgumentPromise');
    }

    function it_adds_ReturnArgumentPromise_during_willReturnArgument_call_with_index_argument($objectProphecy)
    {
        $objectProphecy->addMethodProphecy($this)->willReturn(null);

        $this->willReturnArgument(1);
        $promise = $this->getPromise();
        $promise->shouldBeAnInstanceOf('Prophecy\Promise\ReturnArgumentPromise');
        $promise->execute(array('one', 'two'), $objectProphecy, $this)->shouldReturn('two');
    }

    function it_adds_CallbackPromise_during_will_call_with_callback_argument($objectProphecy)
    {
        $objectProphecy->addMethodProphecy($this)->willReturn(null);

        $callback = function () {};

        $this->will($callback);
        $this->getPromise()->shouldBeAnInstanceOf('Prophecy\Promise\CallbackPromise');
    }

    function it_records_prediction_through_should_method(PredictionInterface $prediction, $objectProphecy)
    {
        $objectProphecy->addMethodProphecy($this)->willReturn(null);

        $this->callOnWrappedObject('should', array($prediction));
        $this->getPrediction()->shouldReturn($prediction);
    }

    function it_adds_CallbackPrediction_during_should_call_with_callback_argument($objectProphecy)
    {
        $objectProphecy->addMethodProphecy($this)->willReturn(null);

        $callback = function () {};

        $this->callOnWrappedObject('should', array($callback));
        $this->getPrediction()->shouldBeAnInstanceOf('Prophecy\Prediction\CallbackPrediction');
    }

    function it_adds_itself_to_ObjectProphecy_during_call_to_should($objectProphecy, PredictionInterface $prediction)
    {
        $objectProphecy->addMethodProphecy($this)->shouldBeCalled();

        $this->callOnWrappedObject('should', array($prediction));
    }

    function it_adds_CallPrediction_during_shouldBeCalled_call($objectProphecy)
    {
        $objectProphecy->addMethodProphecy($this)->willReturn(null);

        $this->callOnWrappedObject('shouldBeCalled', array());
        $this->getPrediction()->shouldBeAnInstanceOf('Prophecy\Prediction\CallPrediction');
    }

    function it_adds_NoCallsPrediction_during_shouldNotBeCalled_call($objectProphecy)
    {
        $objectProphecy->addMethodProphecy($this)->willReturn(null);

        $this->callOnWrappedObject('shouldNotBeCalled', array());
        $this->getPrediction()->shouldBeAnInstanceOf('Prophecy\Prediction\NoCallsPrediction');
    }

    function it_adds_CallTimesPrediction_during_shouldBeCalledTimes_call($objectProphecy)
    {
        $objectProphecy->addMethodProphecy($this)->willReturn(null);

        $this->callOnWrappedObject('shouldBeCalledTimes', array(5));
        $this->getPrediction()->shouldBeAnInstanceOf('Prophecy\Prediction\CallTimesPrediction');
    }

    function it_checks_prediction_via_shouldHave_method_call(
        $objectProphecy,
        ArgumentsWildcard $arguments,
        PredictionInterface $prediction,
        Call $call1,
        Call $call2
    ) {
        $objectProphecy->addMethodProphecy($this)->willReturn(null);
        $prediction->check(array($call1, $call2), $objectProphecy->getWrappedObject(), $this)->shouldBeCalled();
        $objectProphecy->findProphecyMethodCalls('getName', $arguments)->willReturn(array($call1, $call2));

        $this->withArguments($arguments);
        $this->callOnWrappedObject('shouldHave', array($prediction));
    }

    function it_sets_return_promise_during_shouldHave_call_if_none_was_set_before(
        $objectProphecy,
        ArgumentsWildcard $arguments,
        PredictionInterface $prediction,
        Call $call1,
        Call $call2
    ) {
        $objectProphecy->addMethodProphecy($this)->willReturn(null);
        $prediction->check(array($call1, $call2), $objectProphecy->getWrappedObject(), $this)->shouldBeCalled();
        $objectProphecy->findProphecyMethodCalls('getName', $arguments)->willReturn(array($call1, $call2));

        $this->withArguments($arguments);
        $this->callOnWrappedObject('shouldHave', array($prediction));

        $this->getPromise()->shouldReturnAnInstanceOf('Prophecy\Promise\ReturnPromise');
    }

    function it_does_not_set_return_promise_during_shouldHave_call_if_it_was_set_before(
        $objectProphecy,
        ArgumentsWildcard $arguments,
        PredictionInterface $prediction,
        Call $call1,
        Call $call2,
        PromiseInterface $promise
    ) {
        $objectProphecy->addMethodProphecy($this)->willReturn(null);
        $prediction->check(array($call1, $call2), $objectProphecy->getWrappedObject(), $this)->shouldBeCalled();
        $objectProphecy->findProphecyMethodCalls('getName', $arguments)->willReturn(array($call1, $call2));

        $this->will($promise);
        $this->withArguments($arguments);
        $this->callOnWrappedObject('shouldHave', array($prediction));

        $this->getPromise()->shouldReturn($promise);
    }

    function it_records_checked_predictions(
        $objectProphecy,
        ArgumentsWildcard $arguments,
        PredictionInterface $prediction1,
        PredictionInterface $prediction2,
        Call $call1,
        Call $call2,
        PromiseInterface $promise
    ) {
        $objectProphecy->addMethodProphecy($this)->willReturn(null);
        $prediction1->check(array($call1, $call2), $objectProphecy->getWrappedObject(), $this)->willReturn();
        $prediction2->check(array($call1, $call2), $objectProphecy->getWrappedObject(), $this)->willReturn();
        $objectProphecy->findProphecyMethodCalls('getName', $arguments)->willReturn(array($call1, $call2));

        $this->will($promise);
        $this->withArguments($arguments);
        $this->callOnWrappedObject('shouldHave', array($prediction1));
        $this->callOnWrappedObject('shouldHave', array($prediction2));

        $this->getCheckedPredictions()->shouldReturn(array($prediction1, $prediction2));
    }

    function it_records_even_failed_checked_predictions(
        $objectProphecy,
        ArgumentsWildcard $arguments,
        PredictionInterface $prediction,
        Call $call1,
        Call $call2,
        PromiseInterface $promise
    ) {
        $objectProphecy->addMethodProphecy($this)->willReturn(null);
        $prediction->check(array($call1, $call2), $objectProphecy->getWrappedObject(), $this)->willThrow(new \RuntimeException());
        $objectProphecy->findProphecyMethodCalls('getName', $arguments)->willReturn(array($call1, $call2));

        $this->will($promise);
        $this->withArguments($arguments);

        try {
          $this->callOnWrappedObject('shouldHave', array($prediction));
        } catch (\Exception $e) {}

        $this->getCheckedPredictions()->shouldReturn(array($prediction));
    }

    function it_checks_prediction_via_shouldHave_method_call_with_callback(
        $objectProphecy,
        ArgumentsWildcard $arguments,
        Call $call1,
        Call $call2
    ) {
        $callback = function ($calls, $object, $method) {
            throw new \RuntimeException;
        };
        $objectProphecy->findProphecyMethodCalls('getName', $arguments)->willReturn(array($call1, $call2));

        $this->withArguments($arguments);
        $this->shouldThrow('RuntimeException')->duringShouldHave($callback);
    }

    function it_does_nothing_during_checkPrediction_if_no_prediction_set()
    {
        $this->checkPrediction()->shouldReturn(null);
    }

    function it_checks_set_prediction_during_checkPrediction(
        $objectProphecy,
        ArgumentsWildcard $arguments,
        PredictionInterface $prediction,
        Call $call1,
        Call $call2
    ) {
        $prediction->check(array($call1, $call2), $objectProphecy->getWrappedObject(), $this)->shouldBeCalled();
        $objectProphecy->findProphecyMethodCalls('getName', $arguments)->willReturn(array($call1, $call2));
        $objectProphecy->addMethodProphecy($this)->willReturn(null);

        $this->withArguments($arguments);
        $this->callOnWrappedObject('should', array($prediction));
        $this->checkPrediction();
    }

    function it_links_back_to_ObjectProphecy_through_getter($objectProphecy)
    {
        $this->getObjectProphecy()->shouldReturn($objectProphecy);
    }

    function it_has_MethodName()
    {
        $this->getMethodName()->shouldReturn('getName');
    }

    function it_contains_ArgumentsWildcard_it_was_constructed_with($objectProphecy, ArgumentsWildcard $wildcard)
    {
        $this->beConstructedWith($objectProphecy, 'getName', $wildcard);

        $this->getArgumentsWildcard()->shouldReturn($wildcard);
    }

    function its_ArgumentWildcard_is_mutable_through_setter(ArgumentsWildcard $wildcard)
    {
        $this->withArguments($wildcard);

        $this->getArgumentsWildcard()->shouldReturn($wildcard);
    }

    function its_withArguments_transforms_passed_array_into_ArgumentsWildcard()
    {
        $this->withArguments(array(42, 33));

        $wildcard = $this->getArgumentsWildcard();
        $wildcard->shouldNotBe(null);
        $wildcard->__toString()->shouldReturn('exact(42), exact(33)');
    }

    function its_withArguments_throws_exception_if_wrong_arguments_provided()
    {
        $this->shouldThrow('Prophecy\Exception\InvalidArgumentException')->duringWithArguments(42);
    }
}
<?php

namespace spec\Prophecy\Prophecy;

use phpDocumentor\Reflection\DocBlock\Tags\Method;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Prophecy\Argument\ArgumentsWildcard;
use Prophecy\Call\Call;
use Prophecy\Call\CallCenter;
use Prophecy\Doubler\Doubler;
use Prophecy\Doubler\LazyDouble;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ProphecySubjectInterface;
use Prophecy\Prophecy\RevealerInterface;

class ObjectProphecySpec extends ObjectBehavior
{
    function let(LazyDouble $lazyDouble, ProphecySubjectInterface $double)
    {
        $this->beConstructedWith($lazyDouble);

        $lazyDouble->getInstance()->willReturn($double);
    }

    function it_implements_ProphecyInterface()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Prophecy\ProphecyInterface');
    }

    function it_sets_parentClass_during_willExtend_call($lazyDouble)
    {
        $lazyDouble->setParentClass('123')->shouldBeCalled();

        $this->willExtend('123');
    }

    function it_adds_interface_during_willImplement_call($lazyDouble)
    {
        $lazyDouble->addInterface('222')->shouldBeCalled();

        $this->willImplement('222');
    }

    function it_sets_constructor_arguments_during_willBeConstructedWith_call($lazyDouble)
    {
        $lazyDouble->setArguments(array(1, 2, 5))->shouldBeCalled();

        $this->willBeConstructedWith(array(1, 2, 5));
    }

    function it_does_not_have_method_prophecies_by_default()
    {
        $this->getMethodProphecies()->shouldHaveCount(0);
    }

    function it_should_get_method_prophecies_by_method_name(
        MethodProphecy $method1,
        MethodProphecy $method2,
        ArgumentsWildcard $arguments
    ) {
        $method1->getMethodName()->willReturn('getName');
        $method1->getArgumentsWildcard()->willReturn($arguments);
        $method2->getMethodName()->willReturn('setName');
        $method2->getArgumentsWildcard()->willReturn($arguments);

        $this->addMethodProphecy($method1);
        $this->addMethodProphecy($method2);

        $methods = $this->getMethodProphecies('setName');
        $methods->shouldHaveCount(1);
        $methods[0]->getMethodName()->shouldReturn('setName');
    }

    function it_should_return_empty_array_if_no_method_prophecies_found()
    {
        $methods = $this->getMethodProphecies('setName');
        $methods->shouldHaveCount(0);
    }

    function it_should_proxy_makeProphecyMethodCall_to_CallCenter($lazyDouble, CallCenter $callCenter)
    {
        $this->beConstructedWith($lazyDouble, $callCenter);

        $callCenter->makeCall($this->getWrappedObject(), 'setName', array('everzet'))->willReturn(42);

        $this->makeProphecyMethodCall('setName', array('everzet'))->shouldReturn(42);
    }

    function it_should_reveal_arguments_and_return_values_from_callCenter(
        $lazyDouble,
        CallCenter $callCenter,
        RevealerInterface $revealer
    ) {
        $this->beConstructedWith($lazyDouble, $callCenter, $revealer);

        $revealer->reveal(array('question'))->willReturn(array('life'));
        $revealer->reveal('answer')->willReturn(42);

        $callCenter->makeCall($this->getWrappedObject(), 'setName', array('life'))->willReturn('answer');

        $this->makeProphecyMethodCall('setName', array('question'))->shouldReturn(42);
    }

    function it_should_proxy_getProphecyMethodCalls_to_CallCenter(
        $lazyDouble,
        CallCenter $callCenter,
        ArgumentsWildcard $wildcard,
        Call $call
    ) {
        $this->beConstructedWith($lazyDouble, $callCenter);

        $callCenter->findCalls('setName', $wildcard)->willReturn(array($call));

        $this->findProphecyMethodCalls('setName', $wildcard)->shouldReturn(array($call));
    }

    function its_addMethodProphecy_adds_method_prophecy(
        MethodProphecy $methodProphecy,
        ArgumentsWildcard $argumentsWildcard
    ) {
        $methodProphecy->getArgumentsWildcard()->willReturn($argumentsWildcard);
        $methodProphecy->getMethodName()->willReturn('getUsername');

        $this->addMethodProphecy($methodProphecy);

        $this->getMethodProphecies()->shouldReturn(array(
            'getUsername' => array($methodProphecy)
        ));
    }

    function its_addMethodProphecy_handles_prophecies_with_different_arguments(
        MethodProphecy $methodProphecy1,
        MethodProphecy $methodProphecy2,
        ArgumentsWildcard $argumentsWildcard1,
        ArgumentsWildcard $argumentsWildcard2
    ) {
        $methodProphecy1->getArgumentsWildcard()->willReturn($argumentsWildcard1);
        $methodProphecy1->getMethodName()->willReturn('getUsername');

        $methodProphecy2->getArgumentsWildcard()->willReturn($argumentsWildcard2);
        $methodProphecy2->getMethodName()->willReturn('getUsername');

        $this->addMethodProphecy($methodProphecy1);
        $this->addMethodProphecy($methodProphecy2);

        $this->getMethodProphecies()->shouldReturn(array(
            'getUsername' => array(
                $methodProphecy1,
                $methodProphecy2,
            )
        ));
    }

    function its_addMethodProphecy_handles_prophecies_for_different_methods(
        MethodProphecy $methodProphecy1,
        MethodProphecy $methodProphecy2,
        ArgumentsWildcard $argumentsWildcard1,
        ArgumentsWildcard $argumentsWildcard2
    ) {
        $methodProphecy1->getArgumentsWildcard()->willReturn($argumentsWildcard1);
        $methodProphecy1->getMethodName()->willReturn('getUsername');

        $methodProphecy2->getArgumentsWildcard()->willReturn($argumentsWildcard2);
        $methodProphecy2->getMethodName()->willReturn('isUsername');

        $this->addMethodProphecy($methodProphecy1);
        $this->addMethodProphecy($methodProphecy2);

        $this->getMethodProphecies()->shouldReturn(array(
            'getUsername' => array(
                $methodProphecy1
            ),
            'isUsername' => array(
                $methodProphecy2
            )
        ));
    }

    function its_addMethodProphecy_throws_exception_when_method_has_no_ArgumentsWildcard(MethodProphecy $methodProphecy)
    {
        $methodProphecy->getArgumentsWildcard()->willReturn(null);
        $methodProphecy->getObjectProphecy()->willReturn($this);
        $methodProphecy->getMethodName()->willReturn('getTitle');

        $this->shouldThrow('Prophecy\Exception\Prophecy\MethodProphecyException')->duringAddMethodProphecy(
            $methodProphecy
        );
    }

    function it_returns_null_after_checkPredictions_call_if_there_is_no_method_prophecies()
    {
        $this->checkProphecyMethodsPredictions()->shouldReturn(null);
    }

    function it_throws_AggregateException_during_checkPredictions_if_predictions_fail(
        MethodProphecy $methodProphecy1, MethodProphecy $methodProphecy2,
        ArgumentsWildcard $argumentsWildcard1,
        ArgumentsWildcard $argumentsWildcard2
    ) {
        $methodProphecy1->getMethodName()->willReturn('getName');
        $methodProphecy1->getArgumentsWildcard()->willReturn($argumentsWildcard1);
        $methodProphecy1->checkPrediction()
            ->willThrow('Prophecy\Exception\Prediction\AggregateException');

        $methodProphecy2->getMethodName()->willReturn('setName');
        $methodProphecy2->getArgumentsWildcard()->willReturn($argumentsWildcard2);
        $methodProphecy2->checkPrediction()
            ->willThrow('Prophecy\Exception\Prediction\AggregateException');

        $this->addMethodProphecy($methodProphecy1);
        $this->addMethodProphecy($methodProphecy2);

        $this->shouldThrow('Prophecy\Exception\Prediction\AggregateException')
            ->duringCheckProphecyMethodsPredictions();
    }

    function it_returns_new_MethodProphecy_instance_for_arbitrary_call(
        Doubler $doubler,
        ProphecySubjectInterface $reflection
    ) {
        $doubler->double(Argument::any())->willReturn($reflection);

        $return = $this->getProphecy();
        $return->shouldBeAnInstanceOf('Prophecy\Prophecy\MethodProphecy');
        $return->getMethodName()->shouldReturn('getProphecy');
    }

    function it_returns_same_MethodProphecy_for_same_registered_signature(
        Doubler $doubler,
        ProphecySubjectInterface $reflection
    ) {
        $doubler->double(Argument::any())->willReturn($reflection);

        $this->addMethodProphecy($methodProphecy1 = $this->getProphecy(1, 2, 3));
        $methodProphecy2 = $this->getProphecy(1, 2, 3);

        $methodProphecy2->shouldBe($methodProphecy1);
    }

    function it_returns_new_MethodProphecy_for_different_signatures(
        Doubler $doubler,
        ProphecySubjectInterface $reflection
    ) {
        $doubler->double(Argument::any())->willReturn($reflection);

        $value = new ObjectProphecySpecFixtureB('ABC');
        $value2 = new ObjectProphecySpecFixtureB('CBA');

        $this->addMethodProphecy($methodProphecy1 = $this->getProphecy(1, 2, 3, $value));
        $methodProphecy2 = $this->getProphecy(1, 2, 3, $value2);

        $methodProphecy2->shouldNotBe($methodProphecy1);
    }

    function it_returns_new_MethodProphecy_for_all_callback_signatures(
        Doubler $doubler,
        ProphecySubjectInterface $reflection
    ) {
        $doubler->double(Argument::any())->willReturn($reflection);

        $this->addMethodProphecy($methodProphecy1 = $this->getProphecy(function(){}));
        $methodProphecy2 = $this->getProphecy(function(){});

        $methodProphecy2->shouldNotBe($methodProphecy1);
    }
}

class ObjectProphecySpecFixtureA
{
	public $errors;
}

class ObjectProphecySpecFixtureB extends ObjectProphecySpecFixtureA
{
    public $errors;
    public $value = null;

    public function __construct($value)
    {
        $this->value = $value;
    }
}
<?php

namespace spec\Prophecy\Prophecy;

use PhpSpec\ObjectBehavior;
use Prophecy\Prophecy\ProphecyInterface;

class RevealerSpec extends ObjectBehavior
{
    function it_is_revealer()
    {
        $this->shouldBeAnInstanceOf('Prophecy\Prophecy\RevealerInterface');
    }

    function it_reveals_single_instance_of_ProphecyInterface(ProphecyInterface $prophecy, \stdClass $object)
    {
        $prophecy->reveal()->willReturn($object);

        $this->reveal($prophecy)->shouldReturn($object);
    }

    function it_reveals_instances_of_ProphecyInterface_inside_array(
        ProphecyInterface $prophecy1,
        ProphecyInterface $prophecy2,
        \stdClass $object1,
        \stdClass $object2
    ) {
        $prophecy1->reveal()->willReturn($object1);
        $prophecy2->reveal()->willReturn($object2);

        $this->reveal(array(
            array('item' => $prophecy2),
            $prophecy1
        ))->shouldReturn(array(
            array('item' => $object2),
            $object1
        ));
    }

    function it_does_not_touch_non_prophecy_interface()
    {
        $this->reveal(42)->shouldReturn(42);
    }
}
<?php

namespace spec\Prophecy;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Prophecy\Argument\ArgumentsWildcard;
use Prophecy\Doubler\Doubler;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ProphecySubjectInterface;

class ProphetSpec extends ObjectBehavior
{
    function let(Doubler $doubler, ProphecySubjectInterface $double)
    {
        $doubler->double(null, array())->willReturn($double);

        $this->beConstructedWith($doubler);
    }

    function it_constructs_new_prophecy_on_prophesize_call()
    {
        $prophecy = $this->prophesize();
        $prophecy->shouldBeAnInstanceOf('Prophecy\Prophecy\ObjectProphecy');
    }

    function it_constructs_new_prophecy_with_parent_class_if_specified($doubler, ProphecySubjectInterface $newDouble)
    {
        $doubler->double(Argument::any(), array())->willReturn($newDouble);

        $this->prophesize('Prophecy\Prophet')->reveal()->shouldReturn($newDouble);
    }

    function it_constructs_new_prophecy_with_interface_if_specified($doubler, ProphecySubjectInterface $newDouble)
    {
        $doubler->double(null, Argument::any())->willReturn($newDouble);

        $this->prophesize('ArrayAccess')->reveal()->shouldReturn($newDouble);
    }

    function it_exposes_all_created_prophecies_through_getter()
    {
        $prophecy1 = $this->prophesize();
        $prophecy2 = $this->prophesize();

        $this->getProphecies()->shouldReturn(array($prophecy1, $prophecy2));
    }

    function it_does_nothing_during_checkPredictions_call_if_no_predictions_defined()
    {
        $this->checkPredictions()->shouldReturn(null);
    }

    function it_throws_AggregateException_if_defined_predictions_fail(
        MethodProphecy $method1,
        MethodProphecy $method2,
        ArgumentsWildcard $arguments1,
        ArgumentsWildcard $arguments2
    ) {
        $method1->getMethodName()->willReturn('getName');
        $method1->getArgumentsWildcard()->willReturn($arguments1);
        $method1->checkPrediction()->willReturn(null);

        $method2->getMethodName()->willReturn('isSet');
        $method2->getArgumentsWildcard()->willReturn($arguments2);
        $method2->checkPrediction()->willThrow(
            'Prophecy\Exception\Prediction\AggregateException'
        );

        $this->prophesize()->addMethodProphecy($method1);
        $this->prophesize()->addMethodProphecy($method2);

        $this->shouldThrow('Prophecy\Exception\Prediction\AggregateException')
            ->duringCheckPredictions();
    }

    function it_exposes_doubler_through_getter($doubler)
    {
        $this->getDoubler()->shouldReturn($doubler);
    }
}
<?php

namespace spec\Prophecy\Util;

use PhpSpec\ObjectBehavior;

class StringUtilSpec extends ObjectBehavior
{
    function it_generates_proper_string_representation_for_integer()
    {
        $this->stringify(42)->shouldReturn('42');
    }

    function it_generates_proper_string_representation_for_string()
    {
        $this->stringify('some string')->shouldReturn('"some string"');
    }

    function it_generates_single_line_representation_for_multiline_string()
    {
        $this->stringify("some\nstring")->shouldReturn('"some\\nstring"');
    }

    function it_generates_proper_string_representation_for_double()
    {
        $this->stringify(42.3)->shouldReturn('42.3');
    }

    function it_generates_proper_string_representation_for_boolean_true()
    {
        $this->stringify(true)->shouldReturn('true');
    }

    function it_generates_proper_string_representation_for_boolean_false()
    {
        $this->stringify(false)->shouldReturn('false');
    }

    function it_generates_proper_string_representation_for_null()
    {
        $this->stringify(null)->shouldReturn('null');
    }

    function it_generates_proper_string_representation_for_empty_array()
    {
        $this->stringify(array())->shouldReturn('[]');
    }

    function it_generates_proper_string_representation_for_array()
    {
        $this->stringify(array('zet', 42))->shouldReturn('["zet", 42]');
    }

    function it_generates_proper_string_representation_for_hash_containing_one_value()
    {
        $this->stringify(array('ever' => 'zet'))->shouldReturn('["ever" => "zet"]');
    }

    function it_generates_proper_string_representation_for_hash()
    {
        $this->stringify(array('ever' => 'zet', 52 => 'hey', 'num' => 42))->shouldReturn(
            '["ever" => "zet", 52 => "hey", "num" => 42]'
        );
    }

    function it_generates_proper_string_representation_for_resource()
    {
        $resource = fopen(__FILE__, 'r');
        $this->stringify($resource)->shouldReturn('stream:'.$resource);
    }

    function it_generates_proper_string_representation_for_object(\stdClass $object)
    {
        $objHash = sprintf('%s:%s',
            get_class($object->getWrappedObject()),
            spl_object_hash($object->getWrappedObject())
        ) . " Object (\n    'objectProphecy' => Prophecy\Prophecy\ObjectProphecy Object (*Prophecy*)\n)";

        $this->stringify($object)->shouldReturn("$objHash");
    }

    function it_generates_proper_string_representation_for_object_without_exporting(\stdClass $object)
    {
        $objHash = sprintf('%s:%s',
            get_class($object->getWrappedObject()),
            spl_object_hash($object->getWrappedObject())
        );

        $this->stringify($object, false)->shouldReturn("$objHash");
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Argument;

/**
 * Arguments wildcarding.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class ArgumentsWildcard
{
    /**
     * @var Token\TokenInterface[]
     */
    private $tokens = array();
    private $string;

    /**
     * Initializes wildcard.
     *
     * @param array $arguments Array of argument tokens or values
     */
    public function __construct(array $arguments)
    {
        foreach ($arguments as $argument) {
            if (!$argument instanceof Token\TokenInterface) {
                $argument = new Token\ExactValueToken($argument);
            }

            $this->tokens[] = $argument;
        }
    }

    /**
     * Calculates wildcard match score for provided arguments.
     *
     * @param array $arguments
     *
     * @return false|int False OR integer score (higher - better)
     */
    public function scoreArguments(array $arguments)
    {
        if (0 == count($arguments) && 0 == count($this->tokens)) {
            return 1;
        }

        $arguments  = array_values($arguments);
        $totalScore = 0;
        foreach ($this->tokens as $i => $token) {
            $argument = isset($arguments[$i]) ? $arguments[$i] : null;
            if (1 >= $score = $token->scoreArgument($argument)) {
                return false;
            }

            $totalScore += $score;

            if (true === $token->isLast()) {
                return $totalScore;
            }
        }

        if (count($arguments) > count($this->tokens)) {
            return false;
        }

        return $totalScore;
    }

    /**
     * Returns string representation for wildcard.
     *
     * @return string
     */
    public function __toString()
    {
        if (null === $this->string) {
            $this->string = implode(', ', array_map(function ($token) {
                return (string) $token;
            }, $this->tokens));
        }

        return $this->string;
    }

    /**
     * @return array
     */
    public function getTokens()
    {
        return $this->tokens;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Argument\Token;

/**
 * Any values token.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class AnyValuesToken implements TokenInterface
{
    /**
     * Always scores 2 for any argument.
     *
     * @param $argument
     *
     * @return int
     */
    public function scoreArgument($argument)
    {
        return 2;
    }

    /**
     * Returns true to stop wildcard from processing other tokens.
     *
     * @return bool
     */
    public function isLast()
    {
        return true;
    }

    /**
     * Returns string representation for token.
     *
     * @return string
     */
    public function __toString()
    {
        return '* [, ...]';
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Argument\Token;

/**
 * Any single value token.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class AnyValueToken implements TokenInterface
{
    /**
     * Always scores 3 for any argument.
     *
     * @param $argument
     *
     * @return int
     */
    public function scoreArgument($argument)
    {
        return 3;
    }

    /**
     * Returns false.
     *
     * @return bool
     */
    public function isLast()
    {
        return false;
    }

    /**
     * Returns string representation for token.
     *
     * @return string
     */
    public function __toString()
    {
        return '*';
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Argument\Token;

/**
 * Approximate value token
 *
 * @author Daniel Leech <daniel@dantleech.com>
 */
class ApproximateValueToken implements TokenInterface
{
    private $value;
    private $precision;

    public function __construct($value, $precision = 0)
    {
        $this->value = $value;
        $this->precision = $precision;
    }

    /**
     * {@inheritdoc}
     */
    public function scoreArgument($argument)
    {
        return round($argument, $this->precision) === round($this->value, $this->precision) ? 10 : false;
    }

    /**
     * {@inheritdoc}
     */
    public function isLast()
    {
        return false;
    }

    /**
     * Returns string representation for token.
     *
     * @return string
     */
    public function __toString()
    {
        return sprintf('≅%s', round($this->value, $this->precision));
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Argument\Token;

/**
 * Array elements count token.
 *
 * @author Boris Mikhaylov <kaguxmail@gmail.com>
 */

class ArrayCountToken implements TokenInterface
{
    private $count;

    /**
     * @param integer $value
     */
    public function __construct($value)
    {
        $this->count = $value;
    }

    /**
     * Scores 6 when argument has preset number of elements.
     *
     * @param $argument
     *
     * @return bool|int
     */
    public function scoreArgument($argument)
    {
        return $this->isCountable($argument) && $this->hasProperCount($argument) ? 6 : false;
    }

    /**
     * Returns false.
     *
     * @return boolean
     */
    public function isLast()
    {
        return false;
    }

    /**
     * Returns string representation for token.
     *
     * @return string
     */
    public function __toString()
    {
        return sprintf('count(%s)', $this->count);
    }

    /**
     * Returns true if object is either array or instance of \Countable
     *
     * @param $argument
     * @return bool
     */
    private function isCountable($argument)
    {
        return (is_array($argument) || $argument instanceof \Countable);
    }

    /**
     * Returns true if $argument has expected number of elements
     *
     * @param array|\Countable $argument
     *
     * @return bool
     */
    private function hasProperCount($argument)
    {
        return $this->count === count($argument);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Argument\Token;

use Prophecy\Exception\InvalidArgumentException;

/**
 * Array entry token.
 *
 * @author Boris Mikhaylov <kaguxmail@gmail.com>
 */
class ArrayEntryToken implements TokenInterface
{
    /** @var \Prophecy\Argument\Token\TokenInterface */
    private $key;
    /** @var \Prophecy\Argument\Token\TokenInterface */
    private $value;

    /**
     * @param mixed $key   exact value or token
     * @param mixed $value exact value or token
     */
    public function __construct($key, $value)
    {
        $this->key = $this->wrapIntoExactValueToken($key);
        $this->value = $this->wrapIntoExactValueToken($value);
    }

    /**
     * Scores half of combined scores from key and value tokens for same entry. Capped at 8.
     * If argument implements \ArrayAccess without \Traversable, then key token is restricted to ExactValueToken.
     *
     * @param array|\ArrayAccess|\Traversable $argument
     *
     * @throws \Prophecy\Exception\InvalidArgumentException
     * @return bool|int
     */
    public function scoreArgument($argument)
    {
        if ($argument instanceof \Traversable) {
            $argument = iterator_to_array($argument);
        }

        if ($argument instanceof \ArrayAccess) {
            $argument = $this->convertArrayAccessToEntry($argument);
        }

        if (!is_array($argument) || empty($argument)) {
            return false;
        }

        $keyScores = array_map(array($this->key,'scoreArgument'), array_keys($argument));
        $valueScores = array_map(array($this->value,'scoreArgument'), $argument);
        $scoreEntry = function ($value, $key) {
            return $value && $key ? min(8, ($key + $value) / 2) : false;
        };

        return max(array_map($scoreEntry, $valueScores, $keyScores));
    }

    /**
     * Returns false.
     *
     * @return boolean
     */
    public function isLast()
    {
        return false;
    }

    /**
     * Returns string representation for token.
     *
     * @return string
     */
    public function __toString()
    {
        return sprintf('[..., %s => %s, ...]', $this->key, $this->value);
    }

    /**
     * Returns key
     *
     * @return TokenInterface
     */
    public function getKey()
    {
        return $this->key;
    }

    /**
     * Returns value
     *
     * @return TokenInterface
     */
    public function getValue()
    {
        return $this->value;
    }

    /**
     * Wraps non token $value into ExactValueToken
     *
     * @param $value
     * @return TokenInterface
     */
    private function wrapIntoExactValueToken($value)
    {
        return $value instanceof TokenInterface ? $value : new ExactValueToken($value);
    }

    /**
     * Converts instance of \ArrayAccess to key => value array entry
     *
     * @param \ArrayAccess $object
     *
     * @return array|null
     * @throws \Prophecy\Exception\InvalidArgumentException
     */
    private function convertArrayAccessToEntry(\ArrayAccess $object)
    {
        if (!$this->key instanceof ExactValueToken) {
            throw new InvalidArgumentException(sprintf(
                'You can only use exact value tokens to match key of ArrayAccess object'.PHP_EOL.
                'But you used `%s`.',
                $this->key
            ));
        }

        $key = $this->key->getValue();

        return $object->offsetExists($key) ? array($key => $object[$key]) : array();
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Argument\Token;

/**
 * Array every entry token.
 *
 * @author Adrien Brault <adrien.brault@gmail.com>
 */
class ArrayEveryEntryToken implements TokenInterface
{
    /**
     * @var TokenInterface
     */
    private $value;

    /**
     * @param mixed $value exact value or token
     */
    public function __construct($value)
    {
        if (!$value instanceof TokenInterface) {
            $value = new ExactValueToken($value);
        }

        $this->value = $value;
    }

    /**
     * {@inheritdoc}
     */
    public function scoreArgument($argument)
    {
        if (!$argument instanceof \Traversable && !is_array($argument)) {
            return false;
        }

        $scores = array();
        foreach ($argument as $key => $argumentEntry) {
            $scores[] = $this->value->scoreArgument($argumentEntry);
        }

        if (empty($scores) || in_array(false, $scores, true)) {
            return false;
        }

        return array_sum($scores) / count($scores);
    }

    /**
     * {@inheritdoc}
     */
    public function isLast()
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function __toString()
    {
        return sprintf('[%s, ..., %s]', $this->value, $this->value);
    }

    /**
     * @return TokenInterface
     */
    public function getValue()
    {
        return $this->value;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Argument\Token;

use Prophecy\Exception\InvalidArgumentException;

/**
 * Callback-verified token.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class CallbackToken implements TokenInterface
{
    private $callback;

    /**
     * Initializes token.
     *
     * @param callable $callback
     *
     * @throws \Prophecy\Exception\InvalidArgumentException
     */
    public function __construct($callback)
    {
        if (!is_callable($callback)) {
            throw new InvalidArgumentException(sprintf(
                'Callable expected as an argument to CallbackToken, but got %s.',
                gettype($callback)
            ));
        }

        $this->callback = $callback;
    }

    /**
     * Scores 7 if callback returns true, false otherwise.
     *
     * @param $argument
     *
     * @return bool|int
     */
    public function scoreArgument($argument)
    {
        return call_user_func($this->callback, $argument) ? 7 : false;
    }

    /**
     * Returns false.
     *
     * @return bool
     */
    public function isLast()
    {
        return false;
    }

    /**
     * Returns string representation for token.
     *
     * @return string
     */
    public function __toString()
    {
        return 'callback()';
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Argument\Token;

use SebastianBergmann\Comparator\ComparisonFailure;
use Prophecy\Comparator\Factory as ComparatorFactory;
use Prophecy\Util\StringUtil;

/**
 * Exact value token.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class ExactValueToken implements TokenInterface
{
    private $value;
    private $string;
    private $util;
    private $comparatorFactory;

    /**
     * Initializes token.
     *
     * @param mixed             $value
     * @param StringUtil        $util
     * @param ComparatorFactory $comparatorFactory
     */
    public function __construct($value, StringUtil $util = null, ComparatorFactory $comparatorFactory = null)
    {
        $this->value = $value;
        $this->util  = $util ?: new StringUtil();

        $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance();
    }

    /**
     * Scores 10 if argument matches preset value.
     *
     * @param $argument
     *
     * @return bool|int
     */
    public function scoreArgument($argument)
    {
        if (is_object($argument) && is_object($this->value)) {
            $comparator = $this->comparatorFactory->getComparatorFor(
                $argument, $this->value
            );

            try {
                $comparator->assertEquals($argument, $this->value);
                return 10;
            } catch (ComparisonFailure $failure) {}
        }

        // If either one is an object it should be castable to a string
        if (is_object($argument) xor is_object($this->value)) {
            if (is_object($argument) && !method_exists($argument, '__toString')) {
                return false;
            }

            if (is_object($this->value) && !method_exists($this->value, '__toString')) {
                return false;
            }
        } elseif (is_numeric($argument) && is_numeric($this->value)) {
            // noop
        } elseif (gettype($argument) !== gettype($this->value)) {
            return false;
        }

        return $argument == $this->value ? 10 : false;
    }

    /**
     * Returns preset value against which token checks arguments.
     *
     * @return mixed
     */
    public function getValue()
    {
        return $this->value;
    }

    /**
     * Returns false.
     *
     * @return bool
     */
    public function isLast()
    {
        return false;
    }

    /**
     * Returns string representation for token.
     *
     * @return string
     */
    public function __toString()
    {
        if (null === $this->string) {
            $this->string = sprintf('exact(%s)', $this->util->stringify($this->value));
        }

        return $this->string;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Argument\Token;

use Prophecy\Util\StringUtil;

/**
 * Identical value token.
 *
 * @author Florian Voutzinos <florian@voutzinos.com>
 */
class IdenticalValueToken implements TokenInterface
{
    private $value;
    private $string;
    private $util;

    /**
     * Initializes token.
     *
     * @param mixed      $value
     * @param StringUtil $util
     */
    public function __construct($value, StringUtil $util = null)
    {
        $this->value = $value;
        $this->util  = $util ?: new StringUtil();
    }

    /**
     * Scores 11 if argument matches preset value.
     *
     * @param $argument
     *
     * @return bool|int
     */
    public function scoreArgument($argument)
    {
        return $argument === $this->value ? 11 : false;
    }

    /**
     * Returns false.
     *
     * @return bool
     */
    public function isLast()
    {
        return false;
    }

    /**
     * Returns string representation for token.
     *
     * @return string
     */
    public function __toString()
    {
        if (null === $this->string) {
            $this->string = sprintf('identical(%s)', $this->util->stringify($this->value));
        }

        return $this->string;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Argument\Token;

/**
 * Logical AND token.
 *
 * @author Boris Mikhaylov <kaguxmail@gmail.com>
 */
class LogicalAndToken implements TokenInterface
{
    private $tokens = array();

    /**
     * @param array $arguments exact values or tokens
     */
    public function __construct(array $arguments)
    {
        foreach ($arguments as $argument) {
            if (!$argument instanceof TokenInterface) {
                $argument = new ExactValueToken($argument);
            }
            $this->tokens[] = $argument;
        }
    }

    /**
     * Scores maximum score from scores returned by tokens for this argument if all of them score.
     *
     * @param $argument
     *
     * @return bool|int
     */
    public function scoreArgument($argument)
    {
        if (0 === count($this->tokens)) {
            return false;
        }

        $maxScore = 0;
        foreach ($this->tokens as $token) {
            $score = $token->scoreArgument($argument);
            if (false === $score) {
                return false;
            }
            $maxScore = max($score, $maxScore);
        }

        return $maxScore;
    }

    /**
     * Returns false.
     *
     * @return boolean
     */
    public function isLast()
    {
        return false;
    }

    /**
     * Returns string representation for token.
     *
     * @return string
     */
    public function __toString()
    {
        return sprintf('bool(%s)', implode(' AND ', $this->tokens));
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Argument\Token;

/**
 * Logical NOT token.
 *
 * @author Boris Mikhaylov <kaguxmail@gmail.com>
 */
class LogicalNotToken implements TokenInterface
{
    /** @var \Prophecy\Argument\Token\TokenInterface  */
    private $token;

    /**
     * @param mixed $value exact value or token
     */
    public function __construct($value)
    {
        $this->token = $value instanceof TokenInterface? $value : new ExactValueToken($value);
    }

    /**
     * Scores 4 when preset token does not match the argument.
     *
     * @param $argument
     *
     * @return bool|int
     */
    public function scoreArgument($argument)
    {
        return false === $this->token->scoreArgument($argument) ? 4 : false;
    }

    /**
     * Returns true if preset token is last.
     *
     * @return bool|int
     */
    public function isLast()
    {
        return $this->token->isLast();
    }

    /**
     * Returns originating token.
     *
     * @return TokenInterface
     */
    public function getOriginatingToken()
    {
        return $this->token;
    }

    /**
     * Returns string representation for token.
     *
     * @return string
     */
    public function __toString()
    {
        return sprintf('not(%s)', $this->token);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Argument\Token;

use SebastianBergmann\Comparator\ComparisonFailure;
use Prophecy\Comparator\Factory as ComparatorFactory;
use Prophecy\Util\StringUtil;

/**
 * Object state-checker token.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class ObjectStateToken implements TokenInterface
{
    private $name;
    private $value;
    private $util;
    private $comparatorFactory;

    /**
     * Initializes token.
     *
     * @param string            $methodName
     * @param mixed             $value             Expected return value
     * @param null|StringUtil   $util
     * @param ComparatorFactory $comparatorFactory
     */
    public function __construct(
        $methodName,
        $value,
        StringUtil $util = null,
        ComparatorFactory $comparatorFactory = null
    ) {
        $this->name  = $methodName;
        $this->value = $value;
        $this->util  = $util ?: new StringUtil;

        $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance();
    }

    /**
     * Scores 8 if argument is an object, which method returns expected value.
     *
     * @param mixed $argument
     *
     * @return bool|int
     */
    public function scoreArgument($argument)
    {
        if (is_object($argument) && method_exists($argument, $this->name)) {
            $actual = call_user_func(array($argument, $this->name));

            $comparator = $this->comparatorFactory->getComparatorFor(
                $this->value, $actual
            );

            try {
                $comparator->assertEquals($this->value, $actual);
                return 8;
            } catch (ComparisonFailure $failure) {
                return false;
            }
        }

        if (is_object($argument) && property_exists($argument, $this->name)) {
            return $argument->{$this->name} === $this->value ? 8 : false;
        }

        return false;
    }

    /**
     * Returns false.
     *
     * @return bool
     */
    public function isLast()
    {
        return false;
    }

    /**
     * Returns string representation for token.
     *
     * @return string
     */
    public function __toString()
    {
        return sprintf('state(%s(), %s)',
            $this->name,
            $this->util->stringify($this->value)
        );
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Argument\Token;

/**
 * String contains token.
 *
 * @author Peter Mitchell <pete@peterjmit.com>
 */
class StringContainsToken implements TokenInterface
{
    private $value;

    /**
     * Initializes token.
     *
     * @param string $value
     */
    public function __construct($value)
    {
        $this->value = $value;
    }

    public function scoreArgument($argument)
    {
        return strpos($argument, $this->value) !== false ? 6 : false;
    }

    /**
     * Returns preset value against which token checks arguments.
     *
     * @return mixed
     */
    public function getValue()
    {
        return $this->value;
    }

    /**
     * Returns false.
     *
     * @return bool
     */
    public function isLast()
    {
        return false;
    }

    /**
     * Returns string representation for token.
     *
     * @return string
     */
    public function __toString()
    {
        return sprintf('contains("%s")', $this->value);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Argument\Token;

/**
 * Argument token interface.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
interface TokenInterface
{
    /**
     * Calculates token match score for provided argument.
     *
     * @param $argument
     *
     * @return bool|int
     */
    public function scoreArgument($argument);

    /**
     * Returns true if this token prevents check of other tokens (is last one).
     *
     * @return bool|int
     */
    public function isLast();

    /**
     * Returns string representation for token.
     *
     * @return string
     */
    public function __toString();
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Argument\Token;

use Prophecy\Exception\InvalidArgumentException;

/**
 * Value type token.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class TypeToken implements TokenInterface
{
    private $type;

    /**
     * @param string $type
     */
    public function __construct($type)
    {
        $checker = "is_{$type}";
        if (!function_exists($checker) && !interface_exists($type) && !class_exists($type)) {
            throw new InvalidArgumentException(sprintf(
                'Type or class name expected as an argument to TypeToken, but got %s.', $type
            ));
        }

        $this->type = $type;
    }

    /**
     * Scores 5 if argument has the same type this token was constructed with.
     *
     * @param $argument
     *
     * @return bool|int
     */
    public function scoreArgument($argument)
    {
        $checker = "is_{$this->type}";
        if (function_exists($checker)) {
            return call_user_func($checker, $argument) ? 5 : false;
        }

        return $argument instanceof $this->type ? 5 : false;
    }

    /**
     * Returns false.
     *
     * @return bool
     */
    public function isLast()
    {
        return false;
    }

    /**
     * Returns string representation for token.
     *
     * @return string
     */
    public function __toString()
    {
        return sprintf('type(%s)', $this->type);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy;

use Prophecy\Argument\Token;

/**
 * Argument tokens shortcuts.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class Argument
{
    /**
     * Checks that argument is exact value or object.
     *
     * @param mixed $value
     *
     * @return Token\ExactValueToken
     */
    public static function exact($value)
    {
        return new Token\ExactValueToken($value);
    }

    /**
     * Checks that argument is of specific type or instance of specific class.
     *
     * @param string $type Type name (`integer`, `string`) or full class name
     *
     * @return Token\TypeToken
     */
    public static function type($type)
    {
        return new Token\TypeToken($type);
    }

    /**
     * Checks that argument object has specific state.
     *
     * @param string $methodName
     * @param mixed  $value
     *
     * @return Token\ObjectStateToken
     */
    public static function which($methodName, $value)
    {
        return new Token\ObjectStateToken($methodName, $value);
    }

    /**
     * Checks that argument matches provided callback.
     *
     * @param callable $callback
     *
     * @return Token\CallbackToken
     */
    public static function that($callback)
    {
        return new Token\CallbackToken($callback);
    }

    /**
     * Matches any single value.
     *
     * @return Token\AnyValueToken
     */
    public static function any()
    {
        return new Token\AnyValueToken;
    }

    /**
     * Matches all values to the rest of the signature.
     *
     * @return Token\AnyValuesToken
     */
    public static function cetera()
    {
        return new Token\AnyValuesToken;
    }

    /**
     * Checks that argument matches all tokens
     *
     * @param mixed ... a list of tokens
     *
     * @return Token\LogicalAndToken
     */
    public static function allOf()
    {
        return new Token\LogicalAndToken(func_get_args());
    }

    /**
     * Checks that argument array or countable object has exact number of elements.
     *
     * @param integer $value array elements count
     *
     * @return Token\ArrayCountToken
     */
    public static function size($value)
    {
        return new Token\ArrayCountToken($value);
    }

    /**
     * Checks that argument array contains (key, value) pair
     *
     * @param mixed $key   exact value or token
     * @param mixed $value exact value or token
     *
     * @return Token\ArrayEntryToken
     */
    public static function withEntry($key, $value)
    {
        return new Token\ArrayEntryToken($key, $value);
    }

    /**
     * Checks that arguments array entries all match value
     *
     * @param mixed $value
     *
     * @return Token\ArrayEveryEntryToken
     */
    public static function withEveryEntry($value)
    {
        return new Token\ArrayEveryEntryToken($value);
    }

    /**
     * Checks that argument array contains value
     *
     * @param mixed $value
     *
     * @return Token\ArrayEntryToken
     */
    public static function containing($value)
    {
        return new Token\ArrayEntryToken(self::any(), $value);
    }

    /**
     * Checks that argument array has key
     *
     * @param mixed $key exact value or token
     *
     * @return Token\ArrayEntryToken
     */
    public static function withKey($key)
    {
        return new Token\ArrayEntryToken($key, self::any());
    }

    /**
     * Checks that argument does not match the value|token.
     *
     * @param mixed $value either exact value or argument token
     *
     * @return Token\LogicalNotToken
     */
    public static function not($value)
    {
        return new Token\LogicalNotToken($value);
    }

    /**
     * @param string $value
     *
     * @return Token\StringContainsToken
     */
    public static function containingString($value)
    {
        return new Token\StringContainsToken($value);
    }

    /**
     * Checks that argument is identical value.
     *
     * @param mixed $value
     *
     * @return Token\IdenticalValueToken
     */
    public static function is($value)
    {
        return new Token\IdenticalValueToken($value);
    }

    /**
     * Check that argument is same value when rounding to the
     * given precision.
     *
     * @param float $value
     * @param float $precision
     *
     * @return Token\ApproximateValueToken
     */
    public static function approximate($value, $precision = 0)
    {
        return new Token\ApproximateValueToken($value, $precision);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Call;

use Exception;

/**
 * Call object.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class Call
{
    private $methodName;
    private $arguments;
    private $returnValue;
    private $exception;
    private $file;
    private $line;

    /**
     * Initializes call.
     *
     * @param string      $methodName
     * @param array       $arguments
     * @param mixed       $returnValue
     * @param Exception   $exception
     * @param null|string $file
     * @param null|int    $line
     */
    public function __construct($methodName, array $arguments, $returnValue,
                                Exception $exception = null, $file, $line)
    {
        $this->methodName  = $methodName;
        $this->arguments   = $arguments;
        $this->returnValue = $returnValue;
        $this->exception   = $exception;

        if ($file) {
            $this->file = $file;
            $this->line = intval($line);
        }
    }

    /**
     * Returns called method name.
     *
     * @return string
     */
    public function getMethodName()
    {
        return $this->methodName;
    }

    /**
     * Returns called method arguments.
     *
     * @return array
     */
    public function getArguments()
    {
        return $this->arguments;
    }

    /**
     * Returns called method return value.
     *
     * @return null|mixed
     */
    public function getReturnValue()
    {
        return $this->returnValue;
    }

    /**
     * Returns exception that call thrown.
     *
     * @return null|Exception
     */
    public function getException()
    {
        return $this->exception;
    }

    /**
     * Returns callee filename.
     *
     * @return string
     */
    public function getFile()
    {
        return $this->file;
    }

    /**
     * Returns callee line number.
     *
     * @return int
     */
    public function getLine()
    {
        return $this->line;
    }

    /**
     * Returns short notation for callee place.
     *
     * @return string
     */
    public function getCallPlace()
    {
        if (null === $this->file) {
            return 'unknown';
        }

        return sprintf('%s:%d', $this->file, $this->line);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Call;

use Prophecy\Exception\Prophecy\MethodProphecyException;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Argument\ArgumentsWildcard;
use Prophecy\Util\StringUtil;
use Prophecy\Exception\Call\UnexpectedCallException;

/**
 * Calls receiver & manager.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class CallCenter
{
    private $util;

    /**
     * @var Call[]
     */
    private $recordedCalls = array();

    /**
     * Initializes call center.
     *
     * @param StringUtil $util
     */
    public function __construct(StringUtil $util = null)
    {
        $this->util = $util ?: new StringUtil;
    }

    /**
     * Makes and records specific method call for object prophecy.
     *
     * @param ObjectProphecy $prophecy
     * @param string         $methodName
     * @param array          $arguments
     *
     * @return mixed Returns null if no promise for prophecy found or promise return value.
     *
     * @throws \Prophecy\Exception\Call\UnexpectedCallException If no appropriate method prophecy found
     */
    public function makeCall(ObjectProphecy $prophecy, $methodName, array $arguments)
    {
        // For efficiency exclude 'args' from the generated backtrace
        if (PHP_VERSION_ID >= 50400) {
            // Limit backtrace to last 3 calls as we don't use the rest
            // Limit argument was introduced in PHP 5.4.0
            $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
        } elseif (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) {
            // DEBUG_BACKTRACE_IGNORE_ARGS was introduced in PHP 5.3.6
            $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
        } else {
            $backtrace = debug_backtrace();
        }

        $file = $line = null;
        if (isset($backtrace[2]) && isset($backtrace[2]['file'])) {
            $file = $backtrace[2]['file'];
            $line = $backtrace[2]['line'];
        }

        // If no method prophecies defined, then it's a dummy, so we'll just return null
        if ('__destruct' === $methodName || 0 == count($prophecy->getMethodProphecies())) {
            $this->recordedCalls[] = new Call($methodName, $arguments, null, null, $file, $line);

            return null;
        }

        // There are method prophecies, so it's a fake/stub. Searching prophecy for this call
        $matches = array();
        foreach ($prophecy->getMethodProphecies($methodName) as $methodProphecy) {
            if (0 < $score = $methodProphecy->getArgumentsWildcard()->scoreArguments($arguments)) {
                $matches[] = array($score, $methodProphecy);
            }
        }

        // If fake/stub doesn't have method prophecy for this call - throw exception
        if (!count($matches)) {
            throw $this->createUnexpectedCallException($prophecy, $methodName, $arguments);
        }

        // Sort matches by their score value
        @usort($matches, function ($match1, $match2) { return $match2[0] - $match1[0]; });

        // If Highest rated method prophecy has a promise - execute it or return null instead
        $methodProphecy = $matches[0][1];
        $returnValue = null;
        $exception   = null;
        if ($promise = $methodProphecy->getPromise()) {
            try {
                $returnValue = $promise->execute($arguments, $prophecy, $methodProphecy);
            } catch (\Exception $e) {
                $exception = $e;
            }
        }

        if ($methodProphecy->hasReturnVoid() && $returnValue !== null) {
            throw new MethodProphecyException(
                "The method \"$methodName\" has a void return type, but the promise returned a value",
                $methodProphecy
            );
        }

        $this->recordedCalls[] = new Call(
            $methodName, $arguments, $returnValue, $exception, $file, $line
        );

        if (null !== $exception) {
            throw $exception;
        }

        return $returnValue;
    }

    /**
     * Searches for calls by method name & arguments wildcard.
     *
     * @param string            $methodName
     * @param ArgumentsWildcard $wildcard
     *
     * @return Call[]
     */
    public function findCalls($methodName, ArgumentsWildcard $wildcard)
    {
        return array_values(
            array_filter($this->recordedCalls, function (Call $call) use ($methodName, $wildcard) {
                return $methodName === $call->getMethodName()
                    && 0 < $wildcard->scoreArguments($call->getArguments())
                ;
            })
        );
    }

    private function createUnexpectedCallException(ObjectProphecy $prophecy, $methodName,
                                                   array $arguments)
    {
        $classname = get_class($prophecy->reveal());
        $argstring = implode(', ', array_map(array($this->util, 'stringify'), $arguments));
        $expected  = implode("\n", array_map(function (MethodProphecy $methodProphecy) {
            return sprintf('  - %s(%s)',
                $methodProphecy->getMethodName(),
                $methodProphecy->getArgumentsWildcard()
            );
        }, call_user_func_array('array_merge', $prophecy->getMethodProphecies())));

        return new UnexpectedCallException(
            sprintf(
                "Method call:\n".
                "  - %s(%s)\n".
                "on %s was not expected, expected calls were:\n%s",

                $methodName, $argstring, $classname, $expected
            ),
            $prophecy, $methodName, $arguments
        );
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Comparator;

use SebastianBergmann\Comparator\Comparator;
use SebastianBergmann\Comparator\ComparisonFailure;

/**
 * Closure comparator.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
final class ClosureComparator extends Comparator
{
    public function accepts($expected, $actual)
    {
        return is_object($expected) && $expected instanceof \Closure
            && is_object($actual) && $actual instanceof \Closure;
    }

    public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
    {
        throw new ComparisonFailure(
            $expected,
            $actual,
            // we don't need a diff
            '',
            '',
            false,
            'all closures are born different'
        );
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Comparator;

use SebastianBergmann\Comparator\Factory as BaseFactory;

/**
 * Prophecy comparator factory.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
final class Factory extends BaseFactory
{
    /**
     * @var Factory
     */
    private static $instance;

    public function __construct()
    {
        parent::__construct();

        $this->register(new ClosureComparator());
        $this->register(new ProphecyComparator());
    }

    /**
     * @return Factory
     */
    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new Factory;
        }

        return self::$instance;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Comparator;

use Prophecy\Prophecy\ProphecyInterface;
use SebastianBergmann\Comparator\ObjectComparator;

class ProphecyComparator extends ObjectComparator
{
    public function accepts($expected, $actual)
    {
        return is_object($expected) && is_object($actual) && $actual instanceof ProphecyInterface;
    }

    public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array())
    {
        parent::assertEquals($expected, $actual->reveal(), $delta, $canonicalize, $ignoreCase, $processed);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler;

use ReflectionClass;

/**
 * Cached class doubler.
 * Prevents mirroring/creation of the same structure twice.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class CachedDoubler extends Doubler
{
    private $classes = array();

    /**
     * {@inheritdoc}
     */
    public function registerClassPatch(ClassPatch\ClassPatchInterface $patch)
    {
        $this->classes[] = array();

        parent::registerClassPatch($patch);
    }

    /**
     * {@inheritdoc}
     */
    protected function createDoubleClass(ReflectionClass $class = null, array $interfaces)
    {
        $classId = $this->generateClassId($class, $interfaces);
        if (isset($this->classes[$classId])) {
            return $this->classes[$classId];
        }

        return $this->classes[$classId] = parent::createDoubleClass($class, $interfaces);
    }

    /**
     * @param ReflectionClass   $class
     * @param ReflectionClass[] $interfaces
     *
     * @return string
     */
    private function generateClassId(ReflectionClass $class = null, array $interfaces)
    {
        $parts = array();
        if (null !== $class) {
            $parts[] = $class->getName();
        }
        foreach ($interfaces as $interface) {
            $parts[] = $interface->getName();
        }
        sort($parts);

        return md5(implode('', $parts));
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler\ClassPatch;

use Prophecy\Doubler\Generator\Node\ClassNode;

/**
 * Class patch interface.
 * Class patches extend doubles functionality or help
 * Prophecy to avoid some internal PHP bugs.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
interface ClassPatchInterface
{
    /**
     * Checks if patch supports specific class node.
     *
     * @param ClassNode $node
     *
     * @return bool
     */
    public function supports(ClassNode $node);

    /**
     * Applies patch to the specific class node.
     *
     * @param ClassNode $node
     * @return void
     */
    public function apply(ClassNode $node);

    /**
     * Returns patch priority, which determines when patch will be applied.
     *
     * @return int Priority number (higher - earlier)
     */
    public function getPriority();
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler\ClassPatch;

use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;

/**
 * Disable constructor.
 * Makes all constructor arguments optional.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class DisableConstructorPatch implements ClassPatchInterface
{
    /**
     * Checks if class has `__construct` method.
     *
     * @param ClassNode $node
     *
     * @return bool
     */
    public function supports(ClassNode $node)
    {
        return true;
    }

    /**
     * Makes all class constructor arguments optional.
     *
     * @param ClassNode $node
     */
    public function apply(ClassNode $node)
    {
        if (!$node->hasMethod('__construct')) {
            $node->addMethod(new MethodNode('__construct', ''));

            return;
        }

        $constructor = $node->getMethod('__construct');
        foreach ($constructor->getArguments() as $argument) {
            $argument->setDefault(null);
        }

        $constructor->setCode(<<<PHP
if (0 < func_num_args()) {
    call_user_func_array(array('parent', '__construct'), func_get_args());
}
PHP
        );
    }

    /**
     * Returns patch priority, which determines when patch will be applied.
     *
     * @return int Priority number (higher - earlier)
     */
    public function getPriority()
    {
        return 100;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler\ClassPatch;

use Prophecy\Doubler\Generator\Node\ClassNode;

/**
 * Exception patch for HHVM to remove the stubs from special methods
 *
 * @author Christophe Coevoet <stof@notk.org>
 */
class HhvmExceptionPatch implements ClassPatchInterface
{
    /**
     * Supports exceptions on HHVM.
     *
     * @param ClassNode $node
     *
     * @return bool
     */
    public function supports(ClassNode $node)
    {
        if (!defined('HHVM_VERSION')) {
            return false;
        }

        return 'Exception' === $node->getParentClass() || is_subclass_of($node->getParentClass(), 'Exception');
    }

    /**
     * Removes special exception static methods from the doubled methods.
     *
     * @param ClassNode $node
     *
     * @return void
     */
    public function apply(ClassNode $node)
    {
        if ($node->hasMethod('setTraceOptions')) {
            $node->getMethod('setTraceOptions')->useParentCode();
        }
        if ($node->hasMethod('getTraceOptions')) {
            $node->getMethod('getTraceOptions')->useParentCode();
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getPriority()
    {
        return -50;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler\ClassPatch;

use Prophecy\Doubler\Generator\Node\ClassNode;

/**
 * Remove method functionality from the double which will clash with php keywords.
 *
 * @author Milan Magudia <milan@magudia.com>
 */
class KeywordPatch implements ClassPatchInterface
{
    /**
     * Support any class
     *
     * @param ClassNode $node
     *
     * @return boolean
     */
    public function supports(ClassNode $node)
    {
        return true;
    }

    /**
     * Remove methods that clash with php keywords
     *
     * @param ClassNode $node
     */
    public function apply(ClassNode $node)
    {
        $methodNames = array_keys($node->getMethods());
        $methodsToRemove = array_intersect($methodNames, $this->getKeywords());
        foreach ($methodsToRemove as $methodName) {
            $node->removeMethod($methodName);
        }
    }

    /**
     * Returns patch priority, which determines when patch will be applied.
     *
     * @return int Priority number (higher - earlier)
     */
    public function getPriority() {
        return 49;
    }

    /**
     * Returns array of php keywords.
     *
     * @return array
     */
    private function getKeywords() {

        return array(
            '__halt_compiler',
            'abstract',
            'and',
            'array',
            'as',
            'break',
            'callable',
            'case',
            'catch',
            'class',
            'clone',
            'const',
            'continue',
            'declare',
            'default',
            'die',
            'do',
            'echo',
            'else',
            'elseif',
            'empty',
            'enddeclare',
            'endfor',
            'endforeach',
            'endif',
            'endswitch',
            'endwhile',
            'eval',
            'exit',
            'extends',
            'final',
            'finally',
            'for',
            'foreach',
            'function',
            'global',
            'goto',
            'if',
            'implements',
            'include',
            'include_once',
            'instanceof',
            'insteadof',
            'interface',
            'isset',
            'list',
            'namespace',
            'new',
            'or',
            'print',
            'private',
            'protected',
            'public',
            'require',
            'require_once',
            'return',
            'static',
            'switch',
            'throw',
            'trait',
            'try',
            'unset',
            'use',
            'var',
            'while',
            'xor',
            'yield',
        );
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler\ClassPatch;

use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;
use Prophecy\PhpDocumentor\ClassAndInterfaceTagRetriever;
use Prophecy\PhpDocumentor\MethodTagRetrieverInterface;

/**
 * Discover Magical API using "@method" PHPDoc format.
 *
 * @author Thomas Tourlourat <thomas@tourlourat.com>
 * @author Kévin Dunglas <dunglas@gmail.com>
 * @author Théo FIDRY <theo.fidry@gmail.com>
 */
class MagicCallPatch implements ClassPatchInterface
{
    private $tagRetriever;

    public function __construct(MethodTagRetrieverInterface $tagRetriever = null)
    {
        $this->tagRetriever = null === $tagRetriever ? new ClassAndInterfaceTagRetriever() : $tagRetriever;
    }

    /**
     * Support any class
     *
     * @param ClassNode $node
     *
     * @return boolean
     */
    public function supports(ClassNode $node)
    {
        return true;
    }

    /**
     * Discover Magical API
     *
     * @param ClassNode $node
     */
    public function apply(ClassNode $node)
    {
        $types = array_filter($node->getInterfaces(), function ($interface) {
            return 0 !== strpos($interface, 'Prophecy\\');
        });
        $types[] = $node->getParentClass();

        foreach ($types as $type) {
            $reflectionClass = new \ReflectionClass($type);
            $tagList = $this->tagRetriever->getTagList($reflectionClass);

            foreach($tagList as $tag) {
                $methodName = $tag->getMethodName();

                if (empty($methodName)) {
                    continue;
                }

                if (!$reflectionClass->hasMethod($methodName)) {
                    $methodNode = new MethodNode($methodName);
                    $methodNode->setStatic($tag->isStatic());
                    $node->addMethod($methodNode);
                }
            }
        }
    }

    /**
     * Returns patch priority, which determines when patch will be applied.
     *
     * @return integer Priority number (higher - earlier)
     */
    public function getPriority()
    {
        return 50;
    }
}

<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler\ClassPatch;

use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;
use Prophecy\Doubler\Generator\Node\ArgumentNode;

/**
 * Add Prophecy functionality to the double.
 * This is a core class patch for Prophecy.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class ProphecySubjectPatch implements ClassPatchInterface
{
    /**
     * Always returns true.
     *
     * @param ClassNode $node
     *
     * @return bool
     */
    public function supports(ClassNode $node)
    {
        return true;
    }

    /**
     * Apply Prophecy functionality to class node.
     *
     * @param ClassNode $node
     */
    public function apply(ClassNode $node)
    {
        $node->addInterface('Prophecy\Prophecy\ProphecySubjectInterface');
        $node->addProperty('objectProphecy', 'private');

        foreach ($node->getMethods() as $name => $method) {
            if ('__construct' === strtolower($name)) {
                continue;
            }

            if ($method->getReturnType() === 'void') {
                $method->setCode(
                    '$this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());'
                );
            } else {
                $method->setCode(
                    'return $this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());'
                );
            }
        }

        $prophecySetter = new MethodNode('setProphecy');
        $prophecyArgument = new ArgumentNode('prophecy');
        $prophecyArgument->setTypeHint('Prophecy\Prophecy\ProphecyInterface');
        $prophecySetter->addArgument($prophecyArgument);
        $prophecySetter->setCode('$this->objectProphecy = $prophecy;');

        $prophecyGetter = new MethodNode('getProphecy');
        $prophecyGetter->setCode('return $this->objectProphecy;');

        if ($node->hasMethod('__call')) {
            $__call = $node->getMethod('__call');
        } else {
            $__call = new MethodNode('__call');
            $__call->addArgument(new ArgumentNode('name'));
            $__call->addArgument(new ArgumentNode('arguments'));

            $node->addMethod($__call);
        }

        $__call->setCode(<<<PHP
throw new \Prophecy\Exception\Doubler\MethodNotFoundException(
    sprintf('Method `%s::%s()` not found.', get_class(\$this), func_get_arg(0)),
    \$this->getProphecy(), func_get_arg(0)
);
PHP
        );

        $node->addMethod($prophecySetter);
        $node->addMethod($prophecyGetter);
    }

    /**
     * Returns patch priority, which determines when patch will be applied.
     *
     * @return int Priority number (higher - earlier)
     */
    public function getPriority()
    {
        return 0;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler\ClassPatch;

use Prophecy\Doubler\Generator\Node\ClassNode;

/**
 * ReflectionClass::newInstance patch.
 * Makes first argument of newInstance optional, since it works but signature is misleading
 *
 * @author Florian Klein <florian.klein@free.fr>
 */
class ReflectionClassNewInstancePatch implements ClassPatchInterface
{
    /**
     * Supports ReflectionClass
     *
     * @param ClassNode $node
     *
     * @return bool
     */
    public function supports(ClassNode $node)
    {
        return 'ReflectionClass' === $node->getParentClass();
    }

    /**
     * Updates newInstance's first argument to make it optional
     *
     * @param ClassNode $node
     */
    public function apply(ClassNode $node)
    {
        foreach ($node->getMethod('newInstance')->getArguments() as $argument) {
            $argument->setDefault(null);
        }
    }

    /**
     * Returns patch priority, which determines when patch will be applied.
     *
     * @return int Priority number (higher = earlier)
     */
    public function getPriority()
    {
        return 50;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler\ClassPatch;

use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;

/**
 * SplFileInfo patch.
 * Makes SplFileInfo and derivative classes usable with Prophecy.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class SplFileInfoPatch implements ClassPatchInterface
{
    /**
     * Supports everything that extends SplFileInfo.
     *
     * @param ClassNode $node
     *
     * @return bool
     */
    public function supports(ClassNode $node)
    {
        if (null === $node->getParentClass()) {
            return false;
        }

        return 'SplFileInfo' === $node->getParentClass()
            || is_subclass_of($node->getParentClass(), 'SplFileInfo')
        ;
    }

    /**
     * Updated constructor code to call parent one with dummy file argument.
     *
     * @param ClassNode $node
     */
    public function apply(ClassNode $node)
    {
        if ($node->hasMethod('__construct')) {
            $constructor = $node->getMethod('__construct');
        } else {
            $constructor = new MethodNode('__construct');
            $node->addMethod($constructor);
        }

        if ($this->nodeIsDirectoryIterator($node)) {
            $constructor->setCode('return parent::__construct("' . __DIR__ . '");');

            return;
        }

        if ($this->nodeIsSplFileObject($node)) {
            $constructor->setCode('return parent::__construct("' . __FILE__ .'");');

            return;
        }

        $constructor->useParentCode();
    }

    /**
     * Returns patch priority, which determines when patch will be applied.
     *
     * @return int Priority number (higher - earlier)
     */
    public function getPriority()
    {
        return 50;
    }

    /**
     * @param ClassNode $node
     * @return boolean
     */
    private function nodeIsDirectoryIterator(ClassNode $node)
    {
        $parent = $node->getParentClass();

        return 'DirectoryIterator' === $parent
            || is_subclass_of($parent, 'DirectoryIterator');
    }

    /**
     * @param ClassNode $node
     * @return boolean
     */
    private function nodeIsSplFileObject(ClassNode $node)
    {
        $parent = $node->getParentClass();

        return 'SplFileObject' === $parent
            || is_subclass_of($parent, 'SplFileObject');
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler\ClassPatch;

use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;

/**
 * Traversable interface patch.
 * Forces classes that implement interfaces, that extend Traversable to also implement Iterator.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class TraversablePatch implements ClassPatchInterface
{
    /**
     * Supports nodetree, that implement Traversable, but not Iterator or IteratorAggregate.
     *
     * @param ClassNode $node
     *
     * @return bool
     */
    public function supports(ClassNode $node)
    {
        if (in_array('Iterator', $node->getInterfaces())) {
            return false;
        }
        if (in_array('IteratorAggregate', $node->getInterfaces())) {
            return false;
        }

        foreach ($node->getInterfaces() as $interface) {
            if ('Traversable' !== $interface && !is_subclass_of($interface, 'Traversable')) {
                continue;
            }
            if ('Iterator' === $interface || is_subclass_of($interface, 'Iterator')) {
                continue;
            }
            if ('IteratorAggregate' === $interface || is_subclass_of($interface, 'IteratorAggregate')) {
                continue;
            }

            return true;
        }

        return false;
    }

    /**
     * Forces class to implement Iterator interface.
     *
     * @param ClassNode $node
     */
    public function apply(ClassNode $node)
    {
        $node->addInterface('Iterator');

        $node->addMethod(new MethodNode('current'));
        $node->addMethod(new MethodNode('key'));
        $node->addMethod(new MethodNode('next'));
        $node->addMethod(new MethodNode('rewind'));
        $node->addMethod(new MethodNode('valid'));
    }

    /**
     * Returns patch priority, which determines when patch will be applied.
     *
     * @return int Priority number (higher - earlier)
     */
    public function getPriority()
    {
        return 100;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler;

/**
 * Core double interface.
 * All doubled classes will implement this one.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
interface DoubleInterface
{
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler;

use Doctrine\Instantiator\Instantiator;
use Prophecy\Doubler\ClassPatch\ClassPatchInterface;
use Prophecy\Doubler\Generator\ClassMirror;
use Prophecy\Doubler\Generator\ClassCreator;
use Prophecy\Exception\InvalidArgumentException;
use ReflectionClass;

/**
 * Cached class doubler.
 * Prevents mirroring/creation of the same structure twice.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class Doubler
{
    private $mirror;
    private $creator;
    private $namer;

    /**
     * @var ClassPatchInterface[]
     */
    private $patches = array();

    /**
     * @var \Doctrine\Instantiator\Instantiator
     */
    private $instantiator;

    /**
     * Initializes doubler.
     *
     * @param ClassMirror   $mirror
     * @param ClassCreator  $creator
     * @param NameGenerator $namer
     */
    public function __construct(ClassMirror $mirror = null, ClassCreator $creator = null,
                                NameGenerator $namer = null)
    {
        $this->mirror  = $mirror  ?: new ClassMirror;
        $this->creator = $creator ?: new ClassCreator;
        $this->namer   = $namer   ?: new NameGenerator;
    }

    /**
     * Returns list of registered class patches.
     *
     * @return ClassPatchInterface[]
     */
    public function getClassPatches()
    {
        return $this->patches;
    }

    /**
     * Registers new class patch.
     *
     * @param ClassPatchInterface $patch
     */
    public function registerClassPatch(ClassPatchInterface $patch)
    {
        $this->patches[] = $patch;

        @usort($this->patches, function (ClassPatchInterface $patch1, ClassPatchInterface $patch2) {
            return $patch2->getPriority() - $patch1->getPriority();
        });
    }

    /**
     * Creates double from specific class or/and list of interfaces.
     *
     * @param ReflectionClass   $class
     * @param ReflectionClass[] $interfaces Array of ReflectionClass instances
     * @param array             $args       Constructor arguments
     *
     * @return DoubleInterface
     *
     * @throws \Prophecy\Exception\InvalidArgumentException
     */
    public function double(ReflectionClass $class = null, array $interfaces, array $args = null)
    {
        foreach ($interfaces as $interface) {
            if (!$interface instanceof ReflectionClass) {
                throw new InvalidArgumentException(sprintf(
                    "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n".
                    "a second argument to `Doubler::double(...)`, but got %s.",
                    is_object($interface) ? get_class($interface).' class' : gettype($interface)
                ));
            }
        }

        $classname  = $this->createDoubleClass($class, $interfaces);
        $reflection = new ReflectionClass($classname);

        if (null !== $args) {
            return $reflection->newInstanceArgs($args);
        }
        if ((null === $constructor = $reflection->getConstructor())
            || ($constructor->isPublic() && !$constructor->isFinal())) {
            return $reflection->newInstance();
        }

        if (!$this->instantiator) {
            $this->instantiator = new Instantiator();
        }

        return $this->instantiator->instantiate($classname);
    }

    /**
     * Creates double class and returns its FQN.
     *
     * @param ReflectionClass   $class
     * @param ReflectionClass[] $interfaces
     *
     * @return string
     */
    protected function createDoubleClass(ReflectionClass $class = null, array $interfaces)
    {
        $name = $this->namer->name($class, $interfaces);
        $node = $this->mirror->reflect($class, $interfaces);

        foreach ($this->patches as $patch) {
            if ($patch->supports($node)) {
                $patch->apply($node);
            }
        }

        $this->creator->create($name, $node);

        return $name;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler\Generator;

/**
 * Class code creator.
 * Generates PHP code for specific class node tree.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class ClassCodeGenerator
{
    /**
     * Generates PHP code for class node.
     *
     * @param string         $classname
     * @param Node\ClassNode $class
     *
     * @return string
     */
    public function generate($classname, Node\ClassNode $class)
    {
        $parts     = explode('\\', $classname);
        $classname = array_pop($parts);
        $namespace = implode('\\', $parts);

        $code = sprintf("class %s extends \%s implements %s {\n",
            $classname, $class->getParentClass(), implode(', ',
                array_map(function ($interface) {return '\\'.$interface;}, $class->getInterfaces())
            )
        );

        foreach ($class->getProperties() as $name => $visibility) {
            $code .= sprintf("%s \$%s;\n", $visibility, $name);
        }
        $code .= "\n";

        foreach ($class->getMethods() as $method) {
            $code .= $this->generateMethod($method)."\n";
        }
        $code .= "\n}";

        return sprintf("namespace %s {\n%s\n}", $namespace, $code);
    }

    private function generateMethod(Node\MethodNode $method)
    {
        $php = sprintf("%s %s function %s%s(%s)%s {\n",
            $method->getVisibility(),
            $method->isStatic() ? 'static' : '',
            $method->returnsReference() ? '&':'',
            $method->getName(),
            implode(', ', $this->generateArguments($method->getArguments())),
            $this->getReturnType($method)
        );
        $php .= $method->getCode()."\n";

        return $php.'}';
    }

    /**
     * @return string
     */
    private function getReturnType(Node\MethodNode $method)
    {
        if (version_compare(PHP_VERSION, '7.1', '>=')) {
            if ($method->hasReturnType()) {
                return $method->hasNullableReturnType()
                    ? sprintf(': ?%s', $method->getReturnType())
                    : sprintf(': %s', $method->getReturnType());
            }
        }

        if (version_compare(PHP_VERSION, '7.0', '>=')) {
            return $method->hasReturnType() && $method->getReturnType() !== 'void'
                ? sprintf(': %s', $method->getReturnType())
                : '';
        }

        return '';
    }

    private function generateArguments(array $arguments)
    {
        return array_map(function (Node\ArgumentNode $argument) {
            $php = '';

            if (version_compare(PHP_VERSION, '7.1', '>=')) {
                $php .= $argument->isNullable() ? '?' : '';
            }

            if ($hint = $argument->getTypeHint()) {
                switch ($hint) {
                    case 'array':
                    case 'callable':
                        $php .= $hint;
                        break;

                    case 'iterable':
                        if (version_compare(PHP_VERSION, '7.1', '>=')) {
                            $php .= $hint;
                            break;
                        }

                        $php .= '\\'.$hint;
                        break;

                    case 'string':
                    case 'int':
                    case 'float':
                    case 'bool':
                        if (version_compare(PHP_VERSION, '7.0', '>=')) {
                            $php .= $hint;
                            break;
                        }
                        // Fall-through to default case for PHP 5.x

                    default:
                        $php .= '\\'.$hint;
                }
            }

            $php .= ' '.($argument->isPassedByReference() ? '&' : '');

            $php .= $argument->isVariadic() ? '...' : '';

            $php .= '$'.$argument->getName();

            if ($argument->isOptional() && !$argument->isVariadic()) {
                $php .= ' = '.var_export($argument->getDefault(), true);
            }

            return $php;
        }, $arguments);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler\Generator;

use Prophecy\Exception\Doubler\ClassCreatorException;

/**
 * Class creator.
 * Creates specific class in current environment.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class ClassCreator
{
    private $generator;

    /**
     * Initializes creator.
     *
     * @param ClassCodeGenerator $generator
     */
    public function __construct(ClassCodeGenerator $generator = null)
    {
        $this->generator = $generator ?: new ClassCodeGenerator;
    }

    /**
     * Creates class.
     *
     * @param string         $classname
     * @param Node\ClassNode $class
     *
     * @return mixed
     *
     * @throws \Prophecy\Exception\Doubler\ClassCreatorException
     */
    public function create($classname, Node\ClassNode $class)
    {
        $code = $this->generator->generate($classname, $class);
        $return = eval($code);

        if (!class_exists($classname, false)) {
            if (count($class->getInterfaces())) {
                throw new ClassCreatorException(sprintf(
                    'Could not double `%s` and implement interfaces: [%s].',
                    $class->getParentClass(), implode(', ', $class->getInterfaces())
                ), $class);
            }

            throw new ClassCreatorException(
                sprintf('Could not double `%s`.', $class->getParentClass()),
                $class
            );
        }

        return $return;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler\Generator;

use Prophecy\Exception\InvalidArgumentException;
use Prophecy\Exception\Doubler\ClassMirrorException;
use ReflectionClass;
use ReflectionMethod;
use ReflectionParameter;

/**
 * Class mirror.
 * Core doubler class. Mirrors specific class and/or interfaces into class node tree.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class ClassMirror
{
    private static $reflectableMethods = array(
        '__construct',
        '__destruct',
        '__sleep',
        '__wakeup',
        '__toString',
        '__call',
        '__invoke'
    );

    /**
     * Reflects provided arguments into class node.
     *
     * @param ReflectionClass   $class
     * @param ReflectionClass[] $interfaces
     *
     * @return Node\ClassNode
     *
     * @throws \Prophecy\Exception\InvalidArgumentException
     */
    public function reflect(ReflectionClass $class = null, array $interfaces)
    {
        $node = new Node\ClassNode;

        if (null !== $class) {
            if (true === $class->isInterface()) {
                throw new InvalidArgumentException(sprintf(
                    "Could not reflect %s as a class, because it\n".
                    "is interface - use the second argument instead.",
                    $class->getName()
                ));
            }

            $this->reflectClassToNode($class, $node);
        }

        foreach ($interfaces as $interface) {
            if (!$interface instanceof ReflectionClass) {
                throw new InvalidArgumentException(sprintf(
                    "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n".
                    "a second argument to `ClassMirror::reflect(...)`, but got %s.",
                    is_object($interface) ? get_class($interface).' class' : gettype($interface)
                ));
            }
            if (false === $interface->isInterface()) {
                throw new InvalidArgumentException(sprintf(
                    "Could not reflect %s as an interface, because it\n".
                    "is class - use the first argument instead.",
                    $interface->getName()
                ));
            }

            $this->reflectInterfaceToNode($interface, $node);
        }

        $node->addInterface('Prophecy\Doubler\Generator\ReflectionInterface');

        return $node;
    }

    private function reflectClassToNode(ReflectionClass $class, Node\ClassNode $node)
    {
        if (true === $class->isFinal()) {
            throw new ClassMirrorException(sprintf(
                'Could not reflect class %s as it is marked final.', $class->getName()
            ), $class);
        }

        $node->setParentClass($class->getName());

        foreach ($class->getMethods(ReflectionMethod::IS_ABSTRACT) as $method) {
            if (false === $method->isProtected()) {
                continue;
            }

            $this->reflectMethodToNode($method, $node);
        }

        foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
            if (0 === strpos($method->getName(), '_')
                && !in_array($method->getName(), self::$reflectableMethods)) {
                continue;
            }

            if (true === $method->isFinal()) {
                $node->addUnextendableMethod($method->getName());
                continue;
            }

            $this->reflectMethodToNode($method, $node);
        }
    }

    private function reflectInterfaceToNode(ReflectionClass $interface, Node\ClassNode $node)
    {
        $node->addInterface($interface->getName());

        foreach ($interface->getMethods() as $method) {
            $this->reflectMethodToNode($method, $node);
        }
    }

    private function reflectMethodToNode(ReflectionMethod $method, Node\ClassNode $classNode)
    {
        $node = new Node\MethodNode($method->getName());

        if (true === $method->isProtected()) {
            $node->setVisibility('protected');
        }

        if (true === $method->isStatic()) {
            $node->setStatic();
        }

        if (true === $method->returnsReference()) {
            $node->setReturnsReference();
        }

        if (version_compare(PHP_VERSION, '7.0', '>=') && $method->hasReturnType()) {
            $returnType = (string) $method->getReturnType();
            $returnTypeLower = strtolower($returnType);

            if ('self' === $returnTypeLower) {
                $returnType = $method->getDeclaringClass()->getName();
            }
            if ('parent' === $returnTypeLower) {
                $returnType = $method->getDeclaringClass()->getParentClass()->getName();
            }

            $node->setReturnType($returnType);

            if (version_compare(PHP_VERSION, '7.1', '>=') && $method->getReturnType()->allowsNull()) {
                $node->setNullableReturnType(true);
            }
        }

        if (is_array($params = $method->getParameters()) && count($params)) {
            foreach ($params as $param) {
                $this->reflectArgumentToNode($param, $node);
            }
        }

        $classNode->addMethod($node);
    }

    private function reflectArgumentToNode(ReflectionParameter $parameter, Node\MethodNode $methodNode)
    {
        $name = $parameter->getName() == '...' ? '__dot_dot_dot__' : $parameter->getName();
        $node = new Node\ArgumentNode($name);

        $node->setTypeHint($this->getTypeHint($parameter));

        if ($this->isVariadic($parameter)) {
            $node->setAsVariadic();
        }

        if ($this->hasDefaultValue($parameter)) {
            $node->setDefault($this->getDefaultValue($parameter));
        }

        if ($parameter->isPassedByReference()) {
            $node->setAsPassedByReference();
        }

        $methodNode->addArgument($node);
    }

    private function hasDefaultValue(ReflectionParameter $parameter)
    {
        if ($this->isVariadic($parameter)) {
            return false;
        }

        if ($parameter->isDefaultValueAvailable()) {
            return true;
        }

        return $parameter->isOptional() || $this->isNullable($parameter);
    }

    private function getDefaultValue(ReflectionParameter $parameter)
    {
        if (!$parameter->isDefaultValueAvailable()) {
            return null;
        }

        return $parameter->getDefaultValue();
    }

    private function getTypeHint(ReflectionParameter $parameter)
    {
        if (null !== $className = $this->getParameterClassName($parameter)) {
            return $className;
        }

        if (true === $parameter->isArray()) {
            return 'array';
        }

        if (version_compare(PHP_VERSION, '5.4', '>=') && true === $parameter->isCallable()) {
            return 'callable';
        }

        if (version_compare(PHP_VERSION, '7.0', '>=') && true === $parameter->hasType()) {
            return (string) $parameter->getType();
        }

        return null;
    }

    private function isVariadic(ReflectionParameter $parameter)
    {
        return PHP_VERSION_ID >= 50600 && $parameter->isVariadic();
    }

    private function isNullable(ReflectionParameter $parameter)
    {
        return $parameter->allowsNull() && null !== $this->getTypeHint($parameter);
    }

    private function getParameterClassName(ReflectionParameter $parameter)
    {
        try {
            return $parameter->getClass() ? $parameter->getClass()->getName() : null;
        } catch (\ReflectionException $e) {
            preg_match('/\[\s\<\w+?>\s([\w,\\\]+)/s', $parameter, $matches);

            return isset($matches[1]) ? $matches[1] : null;
        }
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler\Generator\Node;

/**
 * Argument node.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class ArgumentNode
{
    private $name;
    private $typeHint;
    private $default;
    private $optional    = false;
    private $byReference = false;
    private $isVariadic  = false;
    private $isNullable  = false;

    /**
     * @param string $name
     */
    public function __construct($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }

    public function getTypeHint()
    {
        return $this->typeHint;
    }

    public function setTypeHint($typeHint = null)
    {
        $this->typeHint = $typeHint;
    }

    public function hasDefault()
    {
        return $this->isOptional() && !$this->isVariadic();
    }

    public function getDefault()
    {
        return $this->default;
    }

    public function setDefault($default = null)
    {
        $this->optional = true;
        $this->default  = $default;
    }

    public function isOptional()
    {
        return $this->optional;
    }

    public function setAsPassedByReference($byReference = true)
    {
        $this->byReference = $byReference;
    }

    public function isPassedByReference()
    {
        return $this->byReference;
    }

    public function setAsVariadic($isVariadic = true)
    {
        $this->isVariadic = $isVariadic;
    }

    public function isVariadic()
    {
        return $this->isVariadic;
    }

    public function isNullable()
    {
        return $this->isNullable;
    }

    public function setAsNullable($isNullable = true)
    {
        $this->isNullable = $isNullable;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler\Generator\Node;

use Prophecy\Exception\Doubler\MethodNotExtendableException;
use Prophecy\Exception\InvalidArgumentException;

/**
 * Class node.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class ClassNode
{
    private $parentClass = 'stdClass';
    private $interfaces  = array();
    private $properties  = array();
    private $unextendableMethods = array();

    /**
     * @var MethodNode[]
     */
    private $methods     = array();

    public function getParentClass()
    {
        return $this->parentClass;
    }

    /**
     * @param string $class
     */
    public function setParentClass($class)
    {
        $this->parentClass = $class ?: 'stdClass';
    }

    /**
     * @return string[]
     */
    public function getInterfaces()
    {
        return $this->interfaces;
    }

    /**
     * @param string $interface
     */
    public function addInterface($interface)
    {
        if ($this->hasInterface($interface)) {
            return;
        }

        array_unshift($this->interfaces, $interface);
    }

    /**
     * @param string $interface
     *
     * @return bool
     */
    public function hasInterface($interface)
    {
        return in_array($interface, $this->interfaces);
    }

    public function getProperties()
    {
        return $this->properties;
    }

    public function addProperty($name, $visibility = 'public')
    {
        $visibility = strtolower($visibility);

        if (!in_array($visibility, array('public', 'private', 'protected'))) {
            throw new InvalidArgumentException(sprintf(
                '`%s` property visibility is not supported.', $visibility
            ));
        }

        $this->properties[$name] = $visibility;
    }

    /**
     * @return MethodNode[]
     */
    public function getMethods()
    {
        return $this->methods;
    }

    public function addMethod(MethodNode $method)
    {
        if (!$this->isExtendable($method->getName())){
            $message = sprintf(
                'Method `%s` is not extendable, so can not be added.', $method->getName()
            );
            throw new MethodNotExtendableException($message, $this->getParentClass(), $method->getName());
        }
        $this->methods[$method->getName()] = $method;
    }

    public function removeMethod($name)
    {
        unset($this->methods[$name]);
    }

    /**
     * @param string $name
     *
     * @return MethodNode|null
     */
    public function getMethod($name)
    {
        return $this->hasMethod($name) ? $this->methods[$name] : null;
    }

    /**
     * @param string $name
     *
     * @return bool
     */
    public function hasMethod($name)
    {
        return isset($this->methods[$name]);
    }

    /**
     * @return string[]
     */
    public function getUnextendableMethods()
    {
        return $this->unextendableMethods;
    }

    /**
     * @param string $unextendableMethod
     */
    public function addUnextendableMethod($unextendableMethod)
    {
        if (!$this->isExtendable($unextendableMethod)){
            return;
        }
        $this->unextendableMethods[] = $unextendableMethod;
    }

    /**
     * @param string $method
     * @return bool
     */
    public function isExtendable($method)
    {
        return !in_array($method, $this->unextendableMethods);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler\Generator\Node;

use Prophecy\Exception\InvalidArgumentException;

/**
 * Method node.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class MethodNode
{
    private $name;
    private $code;
    private $visibility = 'public';
    private $static = false;
    private $returnsReference = false;
    private $returnType;
    private $nullableReturnType = false;

    /**
     * @var ArgumentNode[]
     */
    private $arguments = array();

    /**
     * @param string $name
     * @param string $code
     */
    public function __construct($name, $code = null)
    {
        $this->name = $name;
        $this->code = $code;
    }

    public function getVisibility()
    {
        return $this->visibility;
    }

    /**
     * @param string $visibility
     */
    public function setVisibility($visibility)
    {
        $visibility = strtolower($visibility);

        if (!in_array($visibility, array('public', 'private', 'protected'))) {
            throw new InvalidArgumentException(sprintf(
                '`%s` method visibility is not supported.', $visibility
            ));
        }

        $this->visibility = $visibility;
    }

    public function isStatic()
    {
        return $this->static;
    }

    public function setStatic($static = true)
    {
        $this->static = (bool) $static;
    }

    public function returnsReference()
    {
        return $this->returnsReference;
    }

    public function setReturnsReference()
    {
        $this->returnsReference = true;
    }

    public function getName()
    {
        return $this->name;
    }

    public function addArgument(ArgumentNode $argument)
    {
        $this->arguments[] = $argument;
    }

    /**
     * @return ArgumentNode[]
     */
    public function getArguments()
    {
        return $this->arguments;
    }

    public function hasReturnType()
    {
        return null !== $this->returnType;
    }

    /**
     * @param string $type
     */
    public function setReturnType($type = null)
    {
        switch ($type) {
            case '':
                $this->returnType = null;
                break;

            case 'string';
            case 'float':
            case 'int':
            case 'bool':
            case 'array':
            case 'callable':
            case 'iterable':
            case 'void':
                $this->returnType = $type;
                break;

            case 'double':
            case 'real':
                $this->returnType = 'float';
                break;

            case 'boolean':
                $this->returnType = 'bool';
                break;

            case 'integer':
                $this->returnType = 'int';
                break;

            default:
                $this->returnType = '\\' . ltrim($type, '\\');
        }
    }

    public function getReturnType()
    {
        return $this->returnType;
    }

    /**
     * @param bool $bool
     */
    public function setNullableReturnType($bool = true)
    {
        $this->nullableReturnType = (bool) $bool;
    }

    /**
     * @return bool
     */
    public function hasNullableReturnType()
    {
        return $this->nullableReturnType;
    }

    /**
     * @param string $code
     */
    public function setCode($code)
    {
        $this->code = $code;
    }

    public function getCode()
    {
        if ($this->returnsReference)
        {
            return "throw new \Prophecy\Exception\Doubler\ReturnByReferenceException('Returning by reference not supported', get_class(\$this), '{$this->name}');";
        }

        return (string) $this->code;
    }

    public function useParentCode()
    {
        $this->code = sprintf(
            'return parent::%s(%s);', $this->getName(), implode(', ',
                array_map(array($this, 'generateArgument'), $this->arguments)
            )
        );
    }

    private function generateArgument(ArgumentNode $arg)
    {
        $argument = '$'.$arg->getName();

        if ($arg->isVariadic()) {
            $argument = '...'.$argument;
        }

        return $argument;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler\Generator;

/**
 * Reflection interface.
 * All reflected classes implement this interface.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
interface ReflectionInterface
{
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler;

use Prophecy\Exception\Doubler\DoubleException;
use Prophecy\Exception\Doubler\ClassNotFoundException;
use Prophecy\Exception\Doubler\InterfaceNotFoundException;
use ReflectionClass;

/**
 * Lazy double.
 * Gives simple interface to describe double before creating it.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class LazyDouble
{
    private $doubler;
    private $class;
    private $interfaces = array();
    private $arguments  = null;
    private $double;

    /**
     * Initializes lazy double.
     *
     * @param Doubler $doubler
     */
    public function __construct(Doubler $doubler)
    {
        $this->doubler = $doubler;
    }

    /**
     * Tells doubler to use specific class as parent one for double.
     *
     * @param string|ReflectionClass $class
     *
     * @throws \Prophecy\Exception\Doubler\ClassNotFoundException
     * @throws \Prophecy\Exception\Doubler\DoubleException
     */
    public function setParentClass($class)
    {
        if (null !== $this->double) {
            throw new DoubleException('Can not extend class with already instantiated double.');
        }

        if (!$class instanceof ReflectionClass) {
            if (!class_exists($class)) {
                throw new ClassNotFoundException(sprintf('Class %s not found.', $class), $class);
            }

            $class = new ReflectionClass($class);
        }

        $this->class = $class;
    }

    /**
     * Tells doubler to implement specific interface with double.
     *
     * @param string|ReflectionClass $interface
     *
     * @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException
     * @throws \Prophecy\Exception\Doubler\DoubleException
     */
    public function addInterface($interface)
    {
        if (null !== $this->double) {
            throw new DoubleException(
                'Can not implement interface with already instantiated double.'
            );
        }

        if (!$interface instanceof ReflectionClass) {
            if (!interface_exists($interface)) {
                throw new InterfaceNotFoundException(
                    sprintf('Interface %s not found.', $interface),
                    $interface
                );
            }

            $interface = new ReflectionClass($interface);
        }

        $this->interfaces[] = $interface;
    }

    /**
     * Sets constructor arguments.
     *
     * @param array $arguments
     */
    public function setArguments(array $arguments = null)
    {
        $this->arguments = $arguments;
    }

    /**
     * Creates double instance or returns already created one.
     *
     * @return DoubleInterface
     */
    public function getInstance()
    {
        if (null === $this->double) {
            if (null !== $this->arguments) {
                return $this->double = $this->doubler->double(
                    $this->class, $this->interfaces, $this->arguments
                );
            }

            $this->double = $this->doubler->double($this->class, $this->interfaces);
        }

        return $this->double;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Doubler;

use ReflectionClass;

/**
 * Name generator.
 * Generates classname for double.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class NameGenerator
{
    private static $counter = 1;

    /**
     * Generates name.
     *
     * @param ReflectionClass   $class
     * @param ReflectionClass[] $interfaces
     *
     * @return string
     */
    public function name(ReflectionClass $class = null, array $interfaces)
    {
        $parts = array();

        if (null !== $class) {
            $parts[] = $class->getName();
        } else {
            foreach ($interfaces as $interface) {
                $parts[] = $interface->getShortName();
            }
        }

        if (!count($parts)) {
            $parts[] = 'stdClass';
        }

        return sprintf('Double\%s\P%d', implode('\\', $parts), self::$counter++);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Call;

use Prophecy\Exception\Prophecy\ObjectProphecyException;
use Prophecy\Prophecy\ObjectProphecy;

class UnexpectedCallException extends ObjectProphecyException
{
    private $methodName;
    private $arguments;

    public function __construct($message, ObjectProphecy $objectProphecy,
                                $methodName, array $arguments)
    {
        parent::__construct($message, $objectProphecy);

        $this->methodName = $methodName;
        $this->arguments = $arguments;
    }

    public function getMethodName()
    {
        return $this->methodName;
    }

    public function getArguments()
    {
        return $this->arguments;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Doubler;

use Prophecy\Doubler\Generator\Node\ClassNode;

class ClassCreatorException extends \RuntimeException implements DoublerException
{
    private $node;

    public function __construct($message, ClassNode $node)
    {
        parent::__construct($message);

        $this->node = $node;
    }

    public function getClassNode()
    {
        return $this->node;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Doubler;

use ReflectionClass;

class ClassMirrorException extends \RuntimeException implements DoublerException
{
    private $class;

    public function __construct($message, ReflectionClass $class)
    {
        parent::__construct($message);

        $this->class = $class;
    }

    public function getReflectedClass()
    {
        return $this->class;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Doubler;

class ClassNotFoundException extends DoubleException
{
    private $classname;

    /**
     * @param string $message
     * @param string $classname
     */
    public function __construct($message, $classname)
    {
        parent::__construct($message);

        $this->classname = $classname;
    }

    public function getClassname()
    {
        return $this->classname;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Doubler;

use RuntimeException;

class DoubleException extends RuntimeException implements DoublerException
{
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Doubler;

use Prophecy\Exception\Exception;

interface DoublerException extends Exception
{
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Doubler;

class InterfaceNotFoundException extends ClassNotFoundException
{
    public function getInterfaceName()
    {
        return $this->getClassname();
    }
}
<?php

    namespace Prophecy\Exception\Doubler;

    class MethodNotExtendableException extends DoubleException
    {
        private $methodName;

        private $className;

        /**
         * @param string $message
         * @param string $className
         * @param string $methodName
         */
        public function __construct($message, $className, $methodName)
        {
            parent::__construct($message);

            $this->methodName = $methodName;
            $this->className = $className;
        }


        /**
         * @return string
         */
        public function getMethodName()
        {
            return $this->methodName;
        }

        /**
         * @return string
         */
        public function getClassName()
        {
            return $this->className;
        }

    }
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Doubler;

class MethodNotFoundException extends DoubleException
{
    /**
     * @var string
     */
    private $classname;

    /**
     * @var string
     */
    private $methodName;

    /**
     * @var array
     */
    private $arguments;

    /**
     * @param string $message
     * @param string $classname
     * @param string $methodName
     * @param null|Argument\ArgumentsWildcard|array $arguments
     */
    public function __construct($message, $classname, $methodName, $arguments = null)
    {
        parent::__construct($message);

        $this->classname  = $classname;
        $this->methodName = $methodName;
        $this->arguments = $arguments;
    }

    public function getClassname()
    {
        return $this->classname;
    }

    public function getMethodName()
    {
        return $this->methodName;
    }

    public function getArguments()
    {
        return $this->arguments;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Doubler;

class ReturnByReferenceException extends DoubleException
{
    private $classname;
    private $methodName;

    /**
     * @param string $message
     * @param string $classname
     * @param string $methodName
     */
    public function __construct($message, $classname, $methodName)
    {
        parent::__construct($message);

        $this->classname  = $classname;
        $this->methodName = $methodName;
    }

    public function getClassname()
    {
        return $this->classname;
    }

    public function getMethodName()
    {
        return $this->methodName;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception;

/**
 * Core Prophecy exception interface.
 * All Prophecy exceptions implement it.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
interface Exception
{
    /**
     * @return string
     */
    public function getMessage();
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception;

class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Prediction;

use Prophecy\Prophecy\ObjectProphecy;

class AggregateException extends \RuntimeException implements PredictionException
{
    private $exceptions = array();
    private $objectProphecy;

    public function append(PredictionException $exception)
    {
        $message = $exception->getMessage();
        $message = '  '.strtr($message, array("\n" => "\n  "))."\n";

        $this->message      = rtrim($this->message.$message);
        $this->exceptions[] = $exception;
    }

    /**
     * @return PredictionException[]
     */
    public function getExceptions()
    {
        return $this->exceptions;
    }

    public function setObjectProphecy(ObjectProphecy $objectProphecy)
    {
        $this->objectProphecy = $objectProphecy;
    }

    /**
     * @return ObjectProphecy
     */
    public function getObjectProphecy()
    {
        return $this->objectProphecy;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Prediction;

use RuntimeException;

/**
 * Basic failed prediction exception.
 * Use it for custom prediction failures.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class FailedPredictionException extends RuntimeException implements PredictionException
{
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Prediction;

use Prophecy\Exception\Prophecy\MethodProphecyException;

class NoCallsException extends MethodProphecyException implements PredictionException
{
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Prediction;

use Prophecy\Exception\Exception;

interface PredictionException extends Exception
{
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Prediction;

use Prophecy\Prophecy\MethodProphecy;

class UnexpectedCallsCountException extends UnexpectedCallsException
{
    private $expectedCount;

    public function __construct($message, MethodProphecy $methodProphecy, $count, array $calls)
    {
        parent::__construct($message, $methodProphecy, $calls);

        $this->expectedCount = intval($count);
    }

    public function getExpectedCount()
    {
        return $this->expectedCount;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Prediction;

use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Exception\Prophecy\MethodProphecyException;

class UnexpectedCallsException extends MethodProphecyException implements PredictionException
{
    private $calls = array();

    public function __construct($message, MethodProphecy $methodProphecy, array $calls)
    {
        parent::__construct($message, $methodProphecy);

        $this->calls = $calls;
    }

    public function getCalls()
    {
        return $this->calls;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Prophecy;

use Prophecy\Prophecy\MethodProphecy;

class MethodProphecyException extends ObjectProphecyException
{
    private $methodProphecy;

    public function __construct($message, MethodProphecy $methodProphecy)
    {
        parent::__construct($message, $methodProphecy->getObjectProphecy());

        $this->methodProphecy = $methodProphecy;
    }

    /**
     * @return MethodProphecy
     */
    public function getMethodProphecy()
    {
        return $this->methodProphecy;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Prophecy;

use Prophecy\Prophecy\ObjectProphecy;

class ObjectProphecyException extends \RuntimeException implements ProphecyException
{
    private $objectProphecy;

    public function __construct($message, ObjectProphecy $objectProphecy)
    {
        parent::__construct($message);

        $this->objectProphecy = $objectProphecy;
    }

    /**
     * @return ObjectProphecy
     */
    public function getObjectProphecy()
    {
        return $this->objectProphecy;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Exception\Prophecy;

use Prophecy\Exception\Exception;

interface ProphecyException extends Exception
{
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\PhpDocumentor;

use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag;
use phpDocumentor\Reflection\DocBlock\Tags\Method;

/**
 * @author Théo FIDRY <theo.fidry@gmail.com>
 *
 * @internal
 */
final class ClassAndInterfaceTagRetriever implements MethodTagRetrieverInterface
{
    private $classRetriever;

    public function __construct(MethodTagRetrieverInterface $classRetriever = null)
    {
        if (null !== $classRetriever) {
            $this->classRetriever = $classRetriever;

            return;
        }

        $this->classRetriever = class_exists('phpDocumentor\Reflection\DocBlockFactory') && class_exists('phpDocumentor\Reflection\Types\ContextFactory')
            ? new ClassTagRetriever()
            : new LegacyClassTagRetriever()
        ;
    }

    /**
     * @param \ReflectionClass $reflectionClass
     *
     * @return LegacyMethodTag[]|Method[]
     */
    public function getTagList(\ReflectionClass $reflectionClass)
    {
        return array_merge(
            $this->classRetriever->getTagList($reflectionClass),
            $this->getInterfacesTagList($reflectionClass)
        );
    }

    /**
     * @param \ReflectionClass $reflectionClass
     *
     * @return LegacyMethodTag[]|Method[]
     */
    private function getInterfacesTagList(\ReflectionClass $reflectionClass)
    {
        $interfaces = $reflectionClass->getInterfaces();
        $tagList = array();

        foreach($interfaces as $interface) {
            $tagList = array_merge($tagList, $this->classRetriever->getTagList($interface));
        }

        return $tagList;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\PhpDocumentor;

use phpDocumentor\Reflection\DocBlock\Tags\Method;
use phpDocumentor\Reflection\DocBlockFactory;
use phpDocumentor\Reflection\Types\ContextFactory;

/**
 * @author Théo FIDRY <theo.fidry@gmail.com>
 *
 * @internal
 */
final class ClassTagRetriever implements MethodTagRetrieverInterface
{
    private $docBlockFactory;
    private $contextFactory;

    public function __construct()
    {
        $this->docBlockFactory = DocBlockFactory::createInstance();
        $this->contextFactory = new ContextFactory();
    }

    /**
     * @param \ReflectionClass $reflectionClass
     *
     * @return Method[]
     */
    public function getTagList(\ReflectionClass $reflectionClass)
    {
        try {
            $phpdoc = $this->docBlockFactory->create(
                $reflectionClass,
                $this->contextFactory->createFromReflector($reflectionClass)
            );

            return $phpdoc->getTagsByName('method');
        } catch (\InvalidArgumentException $e) {
            return array();
        }
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\PhpDocumentor;

use phpDocumentor\Reflection\DocBlock;
use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag;

/**
 * @author Théo FIDRY <theo.fidry@gmail.com>
 *
 * @internal
 */
final class LegacyClassTagRetriever implements MethodTagRetrieverInterface
{
    /**
     * @param \ReflectionClass $reflectionClass
     *
     * @return LegacyMethodTag[]
     */
    public function getTagList(\ReflectionClass $reflectionClass)
    {
        $phpdoc = new DocBlock($reflectionClass->getDocComment());

        return $phpdoc->getTagsByName('method');
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\PhpDocumentor;

use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag;
use phpDocumentor\Reflection\DocBlock\Tags\Method;

/**
 * @author Théo FIDRY <theo.fidry@gmail.com>
 *
 * @internal
 */
interface MethodTagRetrieverInterface
{
    /**
     * @param \ReflectionClass $reflectionClass
     *
     * @return LegacyMethodTag[]|Method[]
     */
    public function getTagList(\ReflectionClass $reflectionClass);
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Prediction;

use Prophecy\Call\Call;
use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Exception\InvalidArgumentException;
use Closure;

/**
 * Callback prediction.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class CallbackPrediction implements PredictionInterface
{
    private $callback;

    /**
     * Initializes callback prediction.
     *
     * @param callable $callback Custom callback
     *
     * @throws \Prophecy\Exception\InvalidArgumentException
     */
    public function __construct($callback)
    {
        if (!is_callable($callback)) {
            throw new InvalidArgumentException(sprintf(
                'Callable expected as an argument to CallbackPrediction, but got %s.',
                gettype($callback)
            ));
        }

        $this->callback = $callback;
    }

    /**
     * Executes preset callback.
     *
     * @param Call[]         $calls
     * @param ObjectProphecy $object
     * @param MethodProphecy $method
     */
    public function check(array $calls, ObjectProphecy $object, MethodProphecy $method)
    {
        $callback = $this->callback;

        if ($callback instanceof Closure && method_exists('Closure', 'bind')) {
            $callback = Closure::bind($callback, $object);
        }

        call_user_func($callback, $calls, $object, $method);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Prediction;

use Prophecy\Call\Call;
use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Argument\ArgumentsWildcard;
use Prophecy\Argument\Token\AnyValuesToken;
use Prophecy\Util\StringUtil;
use Prophecy\Exception\Prediction\NoCallsException;

/**
 * Call prediction.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class CallPrediction implements PredictionInterface
{
    private $util;

    /**
     * Initializes prediction.
     *
     * @param StringUtil $util
     */
    public function __construct(StringUtil $util = null)
    {
        $this->util = $util ?: new StringUtil;
    }

    /**
     * Tests that there was at least one call.
     *
     * @param Call[]         $calls
     * @param ObjectProphecy $object
     * @param MethodProphecy $method
     *
     * @throws \Prophecy\Exception\Prediction\NoCallsException
     */
    public function check(array $calls, ObjectProphecy $object, MethodProphecy $method)
    {
        if (count($calls)) {
            return;
        }

        $methodCalls = $object->findProphecyMethodCalls(
            $method->getMethodName(),
            new ArgumentsWildcard(array(new AnyValuesToken))
        );

        if (count($methodCalls)) {
            throw new NoCallsException(sprintf(
                "No calls have been made that match:\n".
                "  %s->%s(%s)\n".
                "but expected at least one.\n".
                "Recorded `%s(...)` calls:\n%s",

                get_class($object->reveal()),
                $method->getMethodName(),
                $method->getArgumentsWildcard(),
                $method->getMethodName(),
                $this->util->stringifyCalls($methodCalls)
            ), $method);
        }

        throw new NoCallsException(sprintf(
            "No calls have been made that match:\n".
            "  %s->%s(%s)\n".
            "but expected at least one.",

            get_class($object->reveal()),
            $method->getMethodName(),
            $method->getArgumentsWildcard()
        ), $method);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Prediction;

use Prophecy\Call\Call;
use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Argument\ArgumentsWildcard;
use Prophecy\Argument\Token\AnyValuesToken;
use Prophecy\Util\StringUtil;
use Prophecy\Exception\Prediction\UnexpectedCallsCountException;

/**
 * Prediction interface.
 * Predictions are logical test blocks, tied to `should...` keyword.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class CallTimesPrediction implements PredictionInterface
{
    private $times;
    private $util;

    /**
     * Initializes prediction.
     *
     * @param int        $times
     * @param StringUtil $util
     */
    public function __construct($times, StringUtil $util = null)
    {
        $this->times = intval($times);
        $this->util  = $util ?: new StringUtil;
    }

    /**
     * Tests that there was exact amount of calls made.
     *
     * @param Call[]         $calls
     * @param ObjectProphecy $object
     * @param MethodProphecy $method
     *
     * @throws \Prophecy\Exception\Prediction\UnexpectedCallsCountException
     */
    public function check(array $calls, ObjectProphecy $object, MethodProphecy $method)
    {
        if ($this->times == count($calls)) {
            return;
        }

        $methodCalls = $object->findProphecyMethodCalls(
            $method->getMethodName(),
            new ArgumentsWildcard(array(new AnyValuesToken))
        );

        if (count($calls)) {
            $message = sprintf(
                "Expected exactly %d calls that match:\n".
                "  %s->%s(%s)\n".
                "but %d were made:\n%s",

                $this->times,
                get_class($object->reveal()),
                $method->getMethodName(),
                $method->getArgumentsWildcard(),
                count($calls),
                $this->util->stringifyCalls($calls)
            );
        } elseif (count($methodCalls)) {
            $message = sprintf(
                "Expected exactly %d calls that match:\n".
                "  %s->%s(%s)\n".
                "but none were made.\n".
                "Recorded `%s(...)` calls:\n%s",

                $this->times,
                get_class($object->reveal()),
                $method->getMethodName(),
                $method->getArgumentsWildcard(),
                $method->getMethodName(),
                $this->util->stringifyCalls($methodCalls)
            );
        } else {
            $message = sprintf(
                "Expected exactly %d calls that match:\n".
                "  %s->%s(%s)\n".
                "but none were made.",

                $this->times,
                get_class($object->reveal()),
                $method->getMethodName(),
                $method->getArgumentsWildcard()
            );
        }

        throw new UnexpectedCallsCountException($message, $method, $this->times, $calls);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Prediction;

use Prophecy\Call\Call;
use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Util\StringUtil;
use Prophecy\Exception\Prediction\UnexpectedCallsException;

/**
 * No calls prediction.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class NoCallsPrediction implements PredictionInterface
{
    private $util;

    /**
     * Initializes prediction.
     *
     * @param null|StringUtil $util
     */
    public function __construct(StringUtil $util = null)
    {
        $this->util = $util ?: new StringUtil;
    }

    /**
     * Tests that there were no calls made.
     *
     * @param Call[]         $calls
     * @param ObjectProphecy $object
     * @param MethodProphecy $method
     *
     * @throws \Prophecy\Exception\Prediction\UnexpectedCallsException
     */
    public function check(array $calls, ObjectProphecy $object, MethodProphecy $method)
    {
        if (!count($calls)) {
            return;
        }

        $verb = count($calls) === 1 ? 'was' : 'were';

        throw new UnexpectedCallsException(sprintf(
            "No calls expected that match:\n".
            "  %s->%s(%s)\n".
            "but %d %s made:\n%s",
            get_class($object->reveal()),
            $method->getMethodName(),
            $method->getArgumentsWildcard(),
            count($calls),
            $verb,
            $this->util->stringifyCalls($calls)
        ), $method, $calls);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Prediction;

use Prophecy\Call\Call;
use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophecy\MethodProphecy;

/**
 * Prediction interface.
 * Predictions are logical test blocks, tied to `should...` keyword.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
interface PredictionInterface
{
    /**
     * Tests that double fulfilled prediction.
     *
     * @param Call[]        $calls
     * @param ObjectProphecy $object
     * @param MethodProphecy $method
     *
     * @throws object
     * @return void
     */
    public function check(array $calls, ObjectProphecy $object, MethodProphecy $method);
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Promise;

use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Exception\InvalidArgumentException;
use Closure;

/**
 * Callback promise.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class CallbackPromise implements PromiseInterface
{
    private $callback;

    /**
     * Initializes callback promise.
     *
     * @param callable $callback Custom callback
     *
     * @throws \Prophecy\Exception\InvalidArgumentException
     */
    public function __construct($callback)
    {
        if (!is_callable($callback)) {
            throw new InvalidArgumentException(sprintf(
                'Callable expected as an argument to CallbackPromise, but got %s.',
                gettype($callback)
            ));
        }

        $this->callback = $callback;
    }

    /**
     * Evaluates promise callback.
     *
     * @param array          $args
     * @param ObjectProphecy $object
     * @param MethodProphecy $method
     *
     * @return mixed
     */
    public function execute(array $args, ObjectProphecy $object, MethodProphecy $method)
    {
        $callback = $this->callback;

        if ($callback instanceof Closure && method_exists('Closure', 'bind')) {
            $callback = Closure::bind($callback, $object);
        }

        return call_user_func($callback, $args, $object, $method);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Promise;

use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophecy\MethodProphecy;

/**
 * Promise interface.
 * Promises are logical blocks, tied to `will...` keyword.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
interface PromiseInterface
{
    /**
     * Evaluates promise.
     *
     * @param array          $args
     * @param ObjectProphecy $object
     * @param MethodProphecy $method
     *
     * @return mixed
     */
    public function execute(array $args, ObjectProphecy $object, MethodProphecy $method);
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Promise;

use Prophecy\Exception\InvalidArgumentException;
use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophecy\MethodProphecy;

/**
 * Return argument promise.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class ReturnArgumentPromise implements PromiseInterface
{
    /**
     * @var int
     */
    private $index;

    /**
     * Initializes callback promise.
     *
     * @param int $index The zero-indexed number of the argument to return
     *
     * @throws \Prophecy\Exception\InvalidArgumentException
     */
    public function __construct($index = 0)
    {
        if (!is_int($index) || $index < 0) {
            throw new InvalidArgumentException(sprintf(
                'Zero-based index expected as argument to ReturnArgumentPromise, but got %s.',
                $index
            ));
        }
        $this->index = $index;
    }

    /**
     * Returns nth argument if has one, null otherwise.
     *
     * @param array          $args
     * @param ObjectProphecy $object
     * @param MethodProphecy $method
     *
     * @return null|mixed
     */
    public function execute(array $args, ObjectProphecy $object, MethodProphecy $method)
    {
        return count($args) > $this->index ? $args[$this->index] : null;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Promise;

use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophecy\MethodProphecy;

/**
 * Return promise.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class ReturnPromise implements PromiseInterface
{
    private $returnValues = array();

    /**
     * Initializes promise.
     *
     * @param array $returnValues Array of values
     */
    public function __construct(array $returnValues)
    {
        $this->returnValues = $returnValues;
    }

    /**
     * Returns saved values one by one until last one, then continuously returns last value.
     *
     * @param array          $args
     * @param ObjectProphecy $object
     * @param MethodProphecy $method
     *
     * @return mixed
     */
    public function execute(array $args, ObjectProphecy $object, MethodProphecy $method)
    {
        $value = array_shift($this->returnValues);

        if (!count($this->returnValues)) {
            $this->returnValues[] = $value;
        }

        return $value;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Promise;

use Doctrine\Instantiator\Instantiator;
use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Exception\InvalidArgumentException;
use ReflectionClass;

/**
 * Throw promise.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class ThrowPromise implements PromiseInterface
{
    private $exception;

    /**
     * @var \Doctrine\Instantiator\Instantiator
     */
    private $instantiator;

    /**
     * Initializes promise.
     *
     * @param string|\Exception|\Throwable $exception Exception class name or instance
     *
     * @throws \Prophecy\Exception\InvalidArgumentException
     */
    public function __construct($exception)
    {
        if (is_string($exception)) {
            if (!class_exists($exception) || !$this->isAValidThrowable($exception)) {
                throw new InvalidArgumentException(sprintf(
                    'Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.',
                    $exception
                ));
            }
        } elseif (!$exception instanceof \Exception && !$exception instanceof \Throwable) {
            throw new InvalidArgumentException(sprintf(
                'Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.',
                is_object($exception) ? get_class($exception) : gettype($exception)
            ));
        }

        $this->exception = $exception;
    }

    /**
     * Throws predefined exception.
     *
     * @param array          $args
     * @param ObjectProphecy $object
     * @param MethodProphecy $method
     *
     * @throws object
     */
    public function execute(array $args, ObjectProphecy $object, MethodProphecy $method)
    {
        if (is_string($this->exception)) {
            $classname   = $this->exception;
            $reflection  = new ReflectionClass($classname);
            $constructor = $reflection->getConstructor();

            if ($constructor->isPublic() && 0 == $constructor->getNumberOfRequiredParameters()) {
                throw $reflection->newInstance();
            }

            if (!$this->instantiator) {
                $this->instantiator = new Instantiator();
            }

            throw $this->instantiator->instantiate($classname);
        }

        throw $this->exception;
    }

    /**
     * @param string $exception
     *
     * @return bool
     */
    private function isAValidThrowable($exception)
    {
        return is_a($exception, 'Exception', true) || is_subclass_of($exception, 'Throwable', true);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Prophecy;

use Prophecy\Argument;
use Prophecy\Prophet;
use Prophecy\Promise;
use Prophecy\Prediction;
use Prophecy\Exception\Doubler\MethodNotFoundException;
use Prophecy\Exception\InvalidArgumentException;
use Prophecy\Exception\Prophecy\MethodProphecyException;

/**
 * Method prophecy.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class MethodProphecy
{
    private $objectProphecy;
    private $methodName;
    private $argumentsWildcard;
    private $promise;
    private $prediction;
    private $checkedPredictions = array();
    private $bound = false;
    private $voidReturnType = false;

    /**
     * Initializes method prophecy.
     *
     * @param ObjectProphecy                        $objectProphecy
     * @param string                                $methodName
     * @param null|Argument\ArgumentsWildcard|array $arguments
     *
     * @throws \Prophecy\Exception\Doubler\MethodNotFoundException If method not found
     */
    public function __construct(ObjectProphecy $objectProphecy, $methodName, $arguments = null)
    {
        $double = $objectProphecy->reveal();
        if (!method_exists($double, $methodName)) {
            throw new MethodNotFoundException(sprintf(
                'Method `%s::%s()` is not defined.', get_class($double), $methodName
            ), get_class($double), $methodName, $arguments);
        }

        $this->objectProphecy = $objectProphecy;
        $this->methodName     = $methodName;

        $reflectedMethod = new \ReflectionMethod($double, $methodName);
        if ($reflectedMethod->isFinal()) {
            throw new MethodProphecyException(sprintf(
                "Can not add prophecy for a method `%s::%s()`\n".
                "as it is a final method.",
                get_class($double),
                $methodName
            ), $this);
        }

        if (null !== $arguments) {
            $this->withArguments($arguments);
        }

        if (version_compare(PHP_VERSION, '7.0', '>=') && true === $reflectedMethod->hasReturnType()) {
            $type = (string) $reflectedMethod->getReturnType();

            if ('void' === $type) {
                $this->voidReturnType = true;
                return;
            }

            $this->will(function () use ($type) {
                switch ($type) {
                    case 'string': return '';
                    case 'float':  return 0.0;
                    case 'int':    return 0;
                    case 'bool':   return false;
                    case 'array':  return array();

                    case 'callable':
                    case 'Closure':
                        return function () {};

                    case 'Traversable':
                    case 'Generator':
                        // Remove eval() when minimum version >=5.5
                        /** @var callable $generator */
                        $generator = eval('return function () { yield; };');
                        return $generator();

                    default:
                        $prophet = new Prophet;
                        return $prophet->prophesize($type)->reveal();
                }
            });
        }
    }

    /**
     * Sets argument wildcard.
     *
     * @param array|Argument\ArgumentsWildcard $arguments
     *
     * @return $this
     *
     * @throws \Prophecy\Exception\InvalidArgumentException
     */
    public function withArguments($arguments)
    {
        if (is_array($arguments)) {
            $arguments = new Argument\ArgumentsWildcard($arguments);
        }

        if (!$arguments instanceof Argument\ArgumentsWildcard) {
            throw new InvalidArgumentException(sprintf(
                "Either an array or an instance of ArgumentsWildcard expected as\n".
                'a `MethodProphecy::withArguments()` argument, but got %s.',
                gettype($arguments)
            ));
        }

        $this->argumentsWildcard = $arguments;

        return $this;
    }

    /**
     * Sets custom promise to the prophecy.
     *
     * @param callable|Promise\PromiseInterface $promise
     *
     * @return $this
     *
     * @throws \Prophecy\Exception\InvalidArgumentException
     */
    public function will($promise)
    {
        if (is_callable($promise)) {
            $promise = new Promise\CallbackPromise($promise);
        }

        if (!$promise instanceof Promise\PromiseInterface) {
            throw new InvalidArgumentException(sprintf(
                'Expected callable or instance of PromiseInterface, but got %s.',
                gettype($promise)
            ));
        }

        $this->bindToObjectProphecy();
        $this->promise = $promise;

        return $this;
    }

    /**
     * Sets return promise to the prophecy.
     *
     * @see Prophecy\Promise\ReturnPromise
     *
     * @return $this
     */
    public function willReturn()
    {
        if ($this->voidReturnType) {
            throw new MethodProphecyException(
                "The method \"$this->methodName\" has a void return type, and so cannot return anything",
                $this
            );
        }

        return $this->will(new Promise\ReturnPromise(func_get_args()));
    }

    /**
     * Sets return argument promise to the prophecy.
     *
     * @param int $index The zero-indexed number of the argument to return
     *
     * @see Prophecy\Promise\ReturnArgumentPromise
     *
     * @return $this
     */
    public function willReturnArgument($index = 0)
    {
        if ($this->voidReturnType) {
            throw new MethodProphecyException("The method \"$this->methodName\" has a void return type", $this);
        }

        return $this->will(new Promise\ReturnArgumentPromise($index));
    }

    /**
     * Sets throw promise to the prophecy.
     *
     * @see Prophecy\Promise\ThrowPromise
     *
     * @param string|\Exception $exception Exception class or instance
     *
     * @return $this
     */
    public function willThrow($exception)
    {
        return $this->will(new Promise\ThrowPromise($exception));
    }

    /**
     * Sets custom prediction to the prophecy.
     *
     * @param callable|Prediction\PredictionInterface $prediction
     *
     * @return $this
     *
     * @throws \Prophecy\Exception\InvalidArgumentException
     */
    public function should($prediction)
    {
        if (is_callable($prediction)) {
            $prediction = new Prediction\CallbackPrediction($prediction);
        }

        if (!$prediction instanceof Prediction\PredictionInterface) {
            throw new InvalidArgumentException(sprintf(
                'Expected callable or instance of PredictionInterface, but got %s.',
                gettype($prediction)
            ));
        }

        $this->bindToObjectProphecy();
        $this->prediction = $prediction;

        return $this;
    }

    /**
     * Sets call prediction to the prophecy.
     *
     * @see Prophecy\Prediction\CallPrediction
     *
     * @return $this
     */
    public function shouldBeCalled()
    {
        return $this->should(new Prediction\CallPrediction);
    }

    /**
     * Sets no calls prediction to the prophecy.
     *
     * @see Prophecy\Prediction\NoCallsPrediction
     *
     * @return $this
     */
    public function shouldNotBeCalled()
    {
        return $this->should(new Prediction\NoCallsPrediction);
    }

    /**
     * Sets call times prediction to the prophecy.
     *
     * @see Prophecy\Prediction\CallTimesPrediction
     *
     * @param $count
     *
     * @return $this
     */
    public function shouldBeCalledTimes($count)
    {
        return $this->should(new Prediction\CallTimesPrediction($count));
    }

    /**
     * Checks provided prediction immediately.
     *
     * @param callable|Prediction\PredictionInterface $prediction
     *
     * @return $this
     *
     * @throws \Prophecy\Exception\InvalidArgumentException
     */
    public function shouldHave($prediction)
    {
        if (is_callable($prediction)) {
            $prediction = new Prediction\CallbackPrediction($prediction);
        }

        if (!$prediction instanceof Prediction\PredictionInterface) {
            throw new InvalidArgumentException(sprintf(
                'Expected callable or instance of PredictionInterface, but got %s.',
                gettype($prediction)
            ));
        }

        if (null === $this->promise && !$this->voidReturnType) {
            $this->willReturn();
        }

        $calls = $this->getObjectProphecy()->findProphecyMethodCalls(
            $this->getMethodName(),
            $this->getArgumentsWildcard()
        );

        try {
            $prediction->check($calls, $this->getObjectProphecy(), $this);
            $this->checkedPredictions[] = $prediction;
        } catch (\Exception $e) {
            $this->checkedPredictions[] = $prediction;

            throw $e;
        }

        return $this;
    }

    /**
     * Checks call prediction.
     *
     * @see Prophecy\Prediction\CallPrediction
     *
     * @return $this
     */
    public function shouldHaveBeenCalled()
    {
        return $this->shouldHave(new Prediction\CallPrediction);
    }

    /**
     * Checks no calls prediction.
     *
     * @see Prophecy\Prediction\NoCallsPrediction
     *
     * @return $this
     */
    public function shouldNotHaveBeenCalled()
    {
        return $this->shouldHave(new Prediction\NoCallsPrediction);
    }

    /**
     * Checks no calls prediction.
     *
     * @see Prophecy\Prediction\NoCallsPrediction
     * @deprecated
     *
     * @return $this
     */
    public function shouldNotBeenCalled()
    {
        return $this->shouldNotHaveBeenCalled();
    }

    /**
     * Checks call times prediction.
     *
     * @see Prophecy\Prediction\CallTimesPrediction
     *
     * @param int $count
     *
     * @return $this
     */
    public function shouldHaveBeenCalledTimes($count)
    {
        return $this->shouldHave(new Prediction\CallTimesPrediction($count));
    }

    /**
     * Checks currently registered [with should(...)] prediction.
     */
    public function checkPrediction()
    {
        if (null === $this->prediction) {
            return;
        }

        $this->shouldHave($this->prediction);
    }

    /**
     * Returns currently registered promise.
     *
     * @return null|Promise\PromiseInterface
     */
    public function getPromise()
    {
        return $this->promise;
    }

    /**
     * Returns currently registered prediction.
     *
     * @return null|Prediction\PredictionInterface
     */
    public function getPrediction()
    {
        return $this->prediction;
    }

    /**
     * Returns predictions that were checked on this object.
     *
     * @return Prediction\PredictionInterface[]
     */
    public function getCheckedPredictions()
    {
        return $this->checkedPredictions;
    }

    /**
     * Returns object prophecy this method prophecy is tied to.
     *
     * @return ObjectProphecy
     */
    public function getObjectProphecy()
    {
        return $this->objectProphecy;
    }

    /**
     * Returns method name.
     *
     * @return string
     */
    public function getMethodName()
    {
        return $this->methodName;
    }

    /**
     * Returns arguments wildcard.
     *
     * @return Argument\ArgumentsWildcard
     */
    public function getArgumentsWildcard()
    {
        return $this->argumentsWildcard;
    }

    /**
     * @return bool
     */
    public function hasReturnVoid()
    {
        return $this->voidReturnType;
    }

    private function bindToObjectProphecy()
    {
        if ($this->bound) {
            return;
        }

        $this->getObjectProphecy()->addMethodProphecy($this);
        $this->bound = true;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Prophecy;

use SebastianBergmann\Comparator\ComparisonFailure;
use Prophecy\Comparator\Factory as ComparatorFactory;
use Prophecy\Call\Call;
use Prophecy\Doubler\LazyDouble;
use Prophecy\Argument\ArgumentsWildcard;
use Prophecy\Call\CallCenter;
use Prophecy\Exception\Prophecy\ObjectProphecyException;
use Prophecy\Exception\Prophecy\MethodProphecyException;
use Prophecy\Exception\Prediction\AggregateException;
use Prophecy\Exception\Prediction\PredictionException;

/**
 * Object prophecy.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class ObjectProphecy implements ProphecyInterface
{
    private $lazyDouble;
    private $callCenter;
    private $revealer;
    private $comparatorFactory;

    /**
     * @var MethodProphecy[][]
     */
    private $methodProphecies = array();

    /**
     * Initializes object prophecy.
     *
     * @param LazyDouble        $lazyDouble
     * @param CallCenter        $callCenter
     * @param RevealerInterface $revealer
     * @param ComparatorFactory $comparatorFactory
     */
    public function __construct(
        LazyDouble $lazyDouble,
        CallCenter $callCenter = null,
        RevealerInterface $revealer = null,
        ComparatorFactory $comparatorFactory = null
    ) {
        $this->lazyDouble = $lazyDouble;
        $this->callCenter = $callCenter ?: new CallCenter;
        $this->revealer   = $revealer ?: new Revealer;

        $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance();
    }

    /**
     * Forces double to extend specific class.
     *
     * @param string $class
     *
     * @return $this
     */
    public function willExtend($class)
    {
        $this->lazyDouble->setParentClass($class);

        return $this;
    }

    /**
     * Forces double to implement specific interface.
     *
     * @param string $interface
     *
     * @return $this
     */
    public function willImplement($interface)
    {
        $this->lazyDouble->addInterface($interface);

        return $this;
    }

    /**
     * Sets constructor arguments.
     *
     * @param array $arguments
     *
     * @return $this
     */
    public function willBeConstructedWith(array $arguments = null)
    {
        $this->lazyDouble->setArguments($arguments);

        return $this;
    }

    /**
     * Reveals double.
     *
     * @return object
     *
     * @throws \Prophecy\Exception\Prophecy\ObjectProphecyException If double doesn't implement needed interface
     */
    public function reveal()
    {
        $double = $this->lazyDouble->getInstance();

        if (null === $double || !$double instanceof ProphecySubjectInterface) {
            throw new ObjectProphecyException(
                "Generated double must implement ProphecySubjectInterface, but it does not.\n".
                'It seems you have wrongly configured doubler without required ClassPatch.',
                $this
            );
        }

        $double->setProphecy($this);

        return $double;
    }

    /**
     * Adds method prophecy to object prophecy.
     *
     * @param MethodProphecy $methodProphecy
     *
     * @throws \Prophecy\Exception\Prophecy\MethodProphecyException If method prophecy doesn't
     *                                                              have arguments wildcard
     */
    public function addMethodProphecy(MethodProphecy $methodProphecy)
    {
        $argumentsWildcard = $methodProphecy->getArgumentsWildcard();
        if (null === $argumentsWildcard) {
            throw new MethodProphecyException(sprintf(
                "Can not add prophecy for a method `%s::%s()`\n".
                "as you did not specify arguments wildcard for it.",
                get_class($this->reveal()),
                $methodProphecy->getMethodName()
            ), $methodProphecy);
        }

        $methodName = $methodProphecy->getMethodName();

        if (!isset($this->methodProphecies[$methodName])) {
            $this->methodProphecies[$methodName] = array();
        }

        $this->methodProphecies[$methodName][] = $methodProphecy;
    }

    /**
     * Returns either all or related to single method prophecies.
     *
     * @param null|string $methodName
     *
     * @return MethodProphecy[]
     */
    public function getMethodProphecies($methodName = null)
    {
        if (null === $methodName) {
            return $this->methodProphecies;
        }

        if (!isset($this->methodProphecies[$methodName])) {
            return array();
        }

        return $this->methodProphecies[$methodName];
    }

    /**
     * Makes specific method call.
     *
     * @param string $methodName
     * @param array  $arguments
     *
     * @return mixed
     */
    public function makeProphecyMethodCall($methodName, array $arguments)
    {
        $arguments = $this->revealer->reveal($arguments);
        $return    = $this->callCenter->makeCall($this, $methodName, $arguments);

        return $this->revealer->reveal($return);
    }

    /**
     * Finds calls by method name & arguments wildcard.
     *
     * @param string            $methodName
     * @param ArgumentsWildcard $wildcard
     *
     * @return Call[]
     */
    public function findProphecyMethodCalls($methodName, ArgumentsWildcard $wildcard)
    {
        return $this->callCenter->findCalls($methodName, $wildcard);
    }

    /**
     * Checks that registered method predictions do not fail.
     *
     * @throws \Prophecy\Exception\Prediction\AggregateException If any of registered predictions fail
     */
    public function checkProphecyMethodsPredictions()
    {
        $exception = new AggregateException(sprintf("%s:\n", get_class($this->reveal())));
        $exception->setObjectProphecy($this);

        foreach ($this->methodProphecies as $prophecies) {
            foreach ($prophecies as $prophecy) {
                try {
                    $prophecy->checkPrediction();
                } catch (PredictionException $e) {
                    $exception->append($e);
                }
            }
        }

        if (count($exception->getExceptions())) {
            throw $exception;
        }
    }

    /**
     * Creates new method prophecy using specified method name and arguments.
     *
     * @param string $methodName
     * @param array  $arguments
     *
     * @return MethodProphecy
     */
    public function __call($methodName, array $arguments)
    {
        $arguments = new ArgumentsWildcard($this->revealer->reveal($arguments));

        foreach ($this->getMethodProphecies($methodName) as $prophecy) {
            $argumentsWildcard = $prophecy->getArgumentsWildcard();
            $comparator = $this->comparatorFactory->getComparatorFor(
                $argumentsWildcard, $arguments
            );

            try {
                $comparator->assertEquals($argumentsWildcard, $arguments);
                return $prophecy;
            } catch (ComparisonFailure $failure) {}
        }

        return new MethodProphecy($this, $methodName, $arguments);
    }

    /**
     * Tries to get property value from double.
     *
     * @param string $name
     *
     * @return mixed
     */
    public function __get($name)
    {
        return $this->reveal()->$name;
    }

    /**
     * Tries to set property value to double.
     *
     * @param string $name
     * @param mixed  $value
     */
    public function __set($name, $value)
    {
        $this->reveal()->$name = $this->revealer->reveal($value);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Prophecy;

/**
 * Core Prophecy interface.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
interface ProphecyInterface
{
    /**
     * Reveals prophecy object (double) .
     *
     * @return object
     */
    public function reveal();
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Prophecy;

/**
 * Controllable doubles interface.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
interface ProphecySubjectInterface
{
    /**
     * Sets subject prophecy.
     *
     * @param ProphecyInterface $prophecy
     */
    public function setProphecy(ProphecyInterface $prophecy);

    /**
     * Returns subject prophecy.
     *
     * @return ProphecyInterface
     */
    public function getProphecy();
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Prophecy;

/**
 * Basic prophecies revealer.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class Revealer implements RevealerInterface
{
    /**
     * Unwraps value(s).
     *
     * @param mixed $value
     *
     * @return mixed
     */
    public function reveal($value)
    {
        if (is_array($value)) {
            return array_map(array($this, __FUNCTION__), $value);
        }

        if (!is_object($value)) {
            return $value;
        }

        if ($value instanceof ProphecyInterface) {
            $value = $value->reveal();
        }

        return $value;
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Prophecy;

/**
 * Prophecies revealer interface.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
interface RevealerInterface
{
    /**
     * Unwraps value(s).
     *
     * @param mixed $value
     *
     * @return mixed
     */
    public function reveal($value);
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy;

use Prophecy\Doubler\Doubler;
use Prophecy\Doubler\LazyDouble;
use Prophecy\Doubler\ClassPatch;
use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophecy\RevealerInterface;
use Prophecy\Prophecy\Revealer;
use Prophecy\Call\CallCenter;
use Prophecy\Util\StringUtil;
use Prophecy\Exception\Prediction\PredictionException;
use Prophecy\Exception\Prediction\AggregateException;

/**
 * Prophet creates prophecies.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class Prophet
{
    private $doubler;
    private $revealer;
    private $util;

    /**
     * @var ObjectProphecy[]
     */
    private $prophecies = array();

    /**
     * Initializes Prophet.
     *
     * @param null|Doubler           $doubler
     * @param null|RevealerInterface $revealer
     * @param null|StringUtil        $util
     */
    public function __construct(Doubler $doubler = null, RevealerInterface $revealer = null,
                                StringUtil $util = null)
    {
        if (null === $doubler) {
            $doubler = new Doubler;
            $doubler->registerClassPatch(new ClassPatch\SplFileInfoPatch);
            $doubler->registerClassPatch(new ClassPatch\TraversablePatch);
            $doubler->registerClassPatch(new ClassPatch\DisableConstructorPatch);
            $doubler->registerClassPatch(new ClassPatch\ProphecySubjectPatch);
            $doubler->registerClassPatch(new ClassPatch\ReflectionClassNewInstancePatch);
            $doubler->registerClassPatch(new ClassPatch\HhvmExceptionPatch());
            $doubler->registerClassPatch(new ClassPatch\MagicCallPatch);
            $doubler->registerClassPatch(new ClassPatch\KeywordPatch);
        }

        $this->doubler  = $doubler;
        $this->revealer = $revealer ?: new Revealer;
        $this->util     = $util ?: new StringUtil;
    }

    /**
     * Creates new object prophecy.
     *
     * @param null|string $classOrInterface Class or interface name
     *
     * @return ObjectProphecy
     */
    public function prophesize($classOrInterface = null)
    {
        $this->prophecies[] = $prophecy = new ObjectProphecy(
            new LazyDouble($this->doubler),
            new CallCenter($this->util),
            $this->revealer
        );

        if ($classOrInterface && class_exists($classOrInterface)) {
            return $prophecy->willExtend($classOrInterface);
        }

        if ($classOrInterface && interface_exists($classOrInterface)) {
            return $prophecy->willImplement($classOrInterface);
        }

        return $prophecy;
    }

    /**
     * Returns all created object prophecies.
     *
     * @return ObjectProphecy[]
     */
    public function getProphecies()
    {
        return $this->prophecies;
    }

    /**
     * Returns Doubler instance assigned to this Prophet.
     *
     * @return Doubler
     */
    public function getDoubler()
    {
        return $this->doubler;
    }

    /**
     * Checks all predictions defined by prophecies of this Prophet.
     *
     * @throws Exception\Prediction\AggregateException If any prediction fails
     */
    public function checkPredictions()
    {
        $exception = new AggregateException("Some predictions failed:\n");
        foreach ($this->prophecies as $prophecy) {
            try {
                $prophecy->checkProphecyMethodsPredictions();
            } catch (PredictionException $e) {
                $exception->append($e);
            }
        }

        if (count($exception->getExceptions())) {
            throw $exception;
        }
    }
}
<?php

namespace Prophecy\Util;

use Prophecy\Prophecy\ProphecyInterface;
use SebastianBergmann\RecursionContext\Context;

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * This class is a modification from sebastianbergmann/exporter
 * @see https://github.com/sebastianbergmann/exporter
 */
class ExportUtil
{
    /**
     * Exports a value as a string
     *
     * The output of this method is similar to the output of print_r(), but
     * improved in various aspects:
     *
     *  - NULL is rendered as "null" (instead of "")
     *  - TRUE is rendered as "true" (instead of "1")
     *  - FALSE is rendered as "false" (instead of "")
     *  - Strings are always quoted with single quotes
     *  - Carriage returns and newlines are normalized to \n
     *  - Recursion and repeated rendering is treated properly
     *
     * @param  mixed  $value
     * @param  int    $indentation The indentation level of the 2nd+ line
     * @return string
     */
    public static function export($value, $indentation = 0)
    {
        return self::recursiveExport($value, $indentation);
    }

    /**
     * Converts an object to an array containing all of its private, protected
     * and public properties.
     *
     * @param  mixed $value
     * @return array
     */
    public static function toArray($value)
    {
        if (!is_object($value)) {
            return (array) $value;
        }

        $array = array();

        foreach ((array) $value as $key => $val) {
            // properties are transformed to keys in the following way:
            // private   $property => "\0Classname\0property"
            // protected $property => "\0*\0property"
            // public    $property => "property"
            if (preg_match('/^\0.+\0(.+)$/', $key, $matches)) {
                $key = $matches[1];
            }

            // See https://github.com/php/php-src/commit/5721132
            if ($key === "\0gcdata") {
                continue;
            }

            $array[$key] = $val;
        }

        // Some internal classes like SplObjectStorage don't work with the
        // above (fast) mechanism nor with reflection in Zend.
        // Format the output similarly to print_r() in this case
        if ($value instanceof \SplObjectStorage) {
            // However, the fast method does work in HHVM, and exposes the
            // internal implementation. Hide it again.
            if (property_exists('\SplObjectStorage', '__storage')) {
                unset($array['__storage']);
            } elseif (property_exists('\SplObjectStorage', 'storage')) {
                unset($array['storage']);
            }

            if (property_exists('\SplObjectStorage', '__key')) {
                unset($array['__key']);
            }

            foreach ($value as $key => $val) {
                $array[spl_object_hash($val)] = array(
                    'obj' => $val,
                    'inf' => $value->getInfo(),
                );
            }
        }

        return $array;
    }

    /**
     * Recursive implementation of export
     *
     * @param  mixed                                       $value       The value to export
     * @param  int                                         $indentation The indentation level of the 2nd+ line
     * @param  \SebastianBergmann\RecursionContext\Context $processed   Previously processed objects
     * @return string
     * @see    SebastianBergmann\Exporter\Exporter::export
     */
    protected static function recursiveExport(&$value, $indentation, $processed = null)
    {
        if ($value === null) {
            return 'null';
        }

        if ($value === true) {
            return 'true';
        }

        if ($value === false) {
            return 'false';
        }

        if (is_float($value) && floatval(intval($value)) === $value) {
            return "$value.0";
        }

        if (is_resource($value)) {
            return sprintf(
                'resource(%d) of type (%s)',
                $value,
                get_resource_type($value)
            );
        }

        if (is_string($value)) {
            // Match for most non printable chars somewhat taking multibyte chars into account
            if (preg_match('/[^\x09-\x0d\x20-\xff]/', $value)) {
                return 'Binary String: 0x' . bin2hex($value);
            }

            return "'" .
            str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) .
            "'";
        }

        $whitespace = str_repeat(' ', 4 * $indentation);

        if (!$processed) {
            $processed = new Context;
        }

        if (is_array($value)) {
            if (($key = $processed->contains($value)) !== false) {
                return 'Array &' . $key;
            }

            $array  = $value;
            $key    = $processed->add($value);
            $values = '';

            if (count($array) > 0) {
                foreach ($array as $k => $v) {
                    $values .= sprintf(
                        '%s    %s => %s' . "\n",
                        $whitespace,
                        self::recursiveExport($k, $indentation),
                        self::recursiveExport($value[$k], $indentation + 1, $processed)
                    );
                }

                $values = "\n" . $values . $whitespace;
            }

            return sprintf('Array &%s (%s)', $key, $values);
        }

        if (is_object($value)) {
            $class = get_class($value);

            if ($value instanceof ProphecyInterface) {
                return sprintf('%s Object (*Prophecy*)', $class);
            } elseif ($hash = $processed->contains($value)) {
                return sprintf('%s:%s Object', $class, $hash);
            }

            $hash   = $processed->add($value);
            $values = '';
            $array  = self::toArray($value);

            if (count($array) > 0) {
                foreach ($array as $k => $v) {
                    $values .= sprintf(
                        '%s    %s => %s' . "\n",
                        $whitespace,
                        self::recursiveExport($k, $indentation),
                        self::recursiveExport($v, $indentation + 1, $processed)
                    );
                }

                $values = "\n" . $values . $whitespace;
            }

            return sprintf('%s:%s Object (%s)', $class, $hash, $values);
        }

        return var_export($value, true);
    }
}
<?php

/*
 * This file is part of the Prophecy.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *     Marcello Duarte <marcello.duarte@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Prophecy\Util;

use Prophecy\Call\Call;

/**
 * String utility.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class StringUtil
{
    /**
     * Stringifies any provided value.
     *
     * @param mixed   $value
     * @param boolean $exportObject
     *
     * @return string
     */
    public function stringify($value, $exportObject = true)
    {
        if (is_array($value)) {
            if (range(0, count($value) - 1) === array_keys($value)) {
                return '['.implode(', ', array_map(array($this, __FUNCTION__), $value)).']';
            }

            $stringify = array($this, __FUNCTION__);

            return '['.implode(', ', array_map(function ($item, $key) use ($stringify) {
                return (is_integer($key) ? $key : '"'.$key.'"').
                    ' => '.call_user_func($stringify, $item);
            }, $value, array_keys($value))).']';
        }
        if (is_resource($value)) {
            return get_resource_type($value).':'.$value;
        }
        if (is_object($value)) {
            return $exportObject ? ExportUtil::export($value) : sprintf('%s:%s', get_class($value), spl_object_hash($value));
        }
        if (true === $value || false === $value) {
            return $value ? 'true' : 'false';
        }
        if (is_string($value)) {
            $str = sprintf('"%s"', str_replace("\n", '\\n', $value));

            if (50 <= strlen($str)) {
                return substr($str, 0, 50).'"...';
            }

            return $str;
        }
        if (null === $value) {
            return 'null';
        }

        return (string) $value;
    }

    /**
     * Stringifies provided array of calls.
     *
     * @param Call[] $calls Array of Call instances
     *
     * @return string
     */
    public function stringifyCalls(array $calls)
    {
        $self = $this;

        return implode(PHP_EOL, array_map(function (Call $call) use ($self) {
            return sprintf('  - %s(%s) @ %s',
                $call->getMethodName(),
                implode(', ', array_map(array($self, 'stringify'), $call->getArguments())),
                str_replace(GETCWD().DIRECTORY_SEPARATOR, '', $call->getCallPlace())
            );
        }, $calls));
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage;

use SebastianBergmann\CodeCoverage\Driver\Driver;
use SebastianBergmann\CodeCoverage\Driver\Xdebug;
use SebastianBergmann\CodeCoverage\Driver\HHVM;
use SebastianBergmann\CodeCoverage\Driver\PHPDBG;
use SebastianBergmann\CodeCoverage\Node\Builder;
use SebastianBergmann\CodeCoverage\Node\Directory;
use SebastianBergmann\CodeUnitReverseLookup\Wizard;
use SebastianBergmann\Environment\Runtime;

/**
 * Provides collection functionality for PHP code coverage information.
 */
class CodeCoverage
{
    /**
     * @var Driver
     */
    private $driver;

    /**
     * @var Filter
     */
    private $filter;

    /**
     * @var Wizard
     */
    private $wizard;

    /**
     * @var bool
     */
    private $cacheTokens = false;

    /**
     * @var bool
     */
    private $checkForUnintentionallyCoveredCode = false;

    /**
     * @var bool
     */
    private $forceCoversAnnotation = false;

    /**
     * @var bool
     */
    private $checkForUnexecutedCoveredCode = false;

    /**
     * @var bool
     */
    private $checkForMissingCoversAnnotation = false;

    /**
     * @var bool
     */
    private $addUncoveredFilesFromWhitelist = true;

    /**
     * @var bool
     */
    private $processUncoveredFilesFromWhitelist = false;

    /**
     * @var bool
     */
    private $ignoreDeprecatedCode = false;

    /**
     * @var mixed
     */
    private $currentId;

    /**
     * Code coverage data.
     *
     * @var array
     */
    private $data = [];

    /**
     * @var array
     */
    private $ignoredLines = [];

    /**
     * @var bool
     */
    private $disableIgnoredLines = false;

    /**
     * Test data.
     *
     * @var array
     */
    private $tests = [];

    /**
     * @var string[]
     */
    private $unintentionallyCoveredSubclassesWhitelist = [];

    /**
     * Determine if the data has been initialized or not
     *
     * @var bool
     */
    private $isInitialized = false;

    /**
     * Determine whether we need to check for dead and unused code on each test
     *
     * @var bool
     */
    private $shouldCheckForDeadAndUnused = true;

    /**
     * Constructor.
     *
     * @param Driver $driver
     * @param Filter $filter
     *
     * @throws RuntimeException
     */
    public function __construct(Driver $driver = null, Filter $filter = null)
    {
        if ($driver === null) {
            $driver = $this->selectDriver();
        }

        if ($filter === null) {
            $filter = new Filter;
        }

        $this->driver = $driver;
        $this->filter = $filter;

        $this->wizard = new Wizard;
    }

    /**
     * Returns the code coverage information as a graph of node objects.
     *
     * @return Directory
     */
    public function getReport()
    {
        $builder = new Builder;

        return $builder->build($this);
    }

    /**
     * Clears collected code coverage data.
     */
    public function clear()
    {
        $this->isInitialized = false;
        $this->currentId     = null;
        $this->data          = [];
        $this->tests         = [];
    }

    /**
     * Returns the filter object used.
     *
     * @return Filter
     */
    public function filter()
    {
        return $this->filter;
    }

    /**
     * Returns the collected code coverage data.
     * Set $raw = true to bypass all filters.
     *
     * @param bool $raw
     *
     * @return array
     */
    public function getData($raw = false)
    {
        if (!$raw && $this->addUncoveredFilesFromWhitelist) {
            $this->addUncoveredFilesFromWhitelist();
        }

        return $this->data;
    }

    /**
     * Sets the coverage data.
     *
     * @param array $data
     */
    public function setData(array $data)
    {
        $this->data = $data;
    }

    /**
     * Returns the test data.
     *
     * @return array
     */
    public function getTests()
    {
        return $this->tests;
    }

    /**
     * Sets the test data.
     *
     * @param array $tests
     */
    public function setTests(array $tests)
    {
        $this->tests = $tests;
    }

    /**
     * Start collection of code coverage information.
     *
     * @param mixed $id
     * @param bool  $clear
     *
     * @throws InvalidArgumentException
     */
    public function start($id, $clear = false)
    {
        if (!is_bool($clear)) {
            throw InvalidArgumentException::create(
                1,
                'boolean'
            );
        }

        if ($clear) {
            $this->clear();
        }

        if ($this->isInitialized === false) {
            $this->initializeData();
        }

        $this->currentId = $id;

        $this->driver->start($this->shouldCheckForDeadAndUnused);
    }

    /**
     * Stop collection of code coverage information.
     *
     * @param bool  $append
     * @param mixed $linesToBeCovered
     * @param array $linesToBeUsed
     *
     * @return array
     *
     * @throws InvalidArgumentException
     */
    public function stop($append = true, $linesToBeCovered = [], array $linesToBeUsed = [])
    {
        if (!is_bool($append)) {
            throw InvalidArgumentException::create(
                1,
                'boolean'
            );
        }

        if (!is_array($linesToBeCovered) && $linesToBeCovered !== false) {
            throw InvalidArgumentException::create(
                2,
                'array or false'
            );
        }

        $data = $this->driver->stop();
        $this->append($data, null, $append, $linesToBeCovered, $linesToBeUsed);

        $this->currentId = null;

        return $data;
    }

    /**
     * Appends code coverage data.
     *
     * @param array $data
     * @param mixed $id
     * @param bool  $append
     * @param mixed $linesToBeCovered
     * @param array $linesToBeUsed
     *
     * @throws RuntimeException
     */
    public function append(array $data, $id = null, $append = true, $linesToBeCovered = [], array $linesToBeUsed = [])
    {
        if ($id === null) {
            $id = $this->currentId;
        }

        if ($id === null) {
            throw new RuntimeException;
        }

        $this->applyListsFilter($data);
        $this->applyIgnoredLinesFilter($data);
        $this->initializeFilesThatAreSeenTheFirstTime($data);

        if (!$append) {
            return;
        }

        if ($id != 'UNCOVERED_FILES_FROM_WHITELIST') {
            $this->applyCoversAnnotationFilter(
                $data,
                $linesToBeCovered,
                $linesToBeUsed
            );
        }

        if (empty($data)) {
            return;
        }

        $size   = 'unknown';
        $status = null;

        if ($id instanceof \PHPUnit_Framework_TestCase) {
            $_size = $id->getSize();

            if ($_size == \PHPUnit_Util_Test::SMALL) {
                $size = 'small';
            } elseif ($_size == \PHPUnit_Util_Test::MEDIUM) {
                $size = 'medium';
            } elseif ($_size == \PHPUnit_Util_Test::LARGE) {
                $size = 'large';
            }

            $status = $id->getStatus();
            $id     = get_class($id) . '::' . $id->getName();
        } elseif ($id instanceof \PHPUnit_Extensions_PhptTestCase) {
            $size = 'large';
            $id   = $id->getName();
        }

        $this->tests[$id] = ['size' => $size, 'status' => $status];

        foreach ($data as $file => $lines) {
            if (!$this->filter->isFile($file)) {
                continue;
            }

            foreach ($lines as $k => $v) {
                if ($v == Driver::LINE_EXECUTED) {
                    if (empty($this->data[$file][$k]) || !in_array($id, $this->data[$file][$k])) {
                        $this->data[$file][$k][] = $id;
                    }
                }
            }
        }
    }

    /**
     * Merges the data from another instance.
     *
     * @param CodeCoverage $that
     */
    public function merge(CodeCoverage $that)
    {
        $this->filter->setWhitelistedFiles(
            array_merge($this->filter->getWhitelistedFiles(), $that->filter()->getWhitelistedFiles())
        );

        foreach ($that->data as $file => $lines) {
            if (!isset($this->data[$file])) {
                if (!$this->filter->isFiltered($file)) {
                    $this->data[$file] = $lines;
                }

                continue;
            }

            foreach ($lines as $line => $data) {
                if ($data !== null) {
                    if (!isset($this->data[$file][$line])) {
                        $this->data[$file][$line] = $data;
                    } else {
                        $this->data[$file][$line] = array_unique(
                            array_merge($this->data[$file][$line], $data)
                        );
                    }
                }
            }
        }

        $this->tests = array_merge($this->tests, $that->getTests());
    }

    /**
     * @param bool $flag
     *
     * @throws InvalidArgumentException
     */
    public function setCacheTokens($flag)
    {
        if (!is_bool($flag)) {
            throw InvalidArgumentException::create(
                1,
                'boolean'
            );
        }

        $this->cacheTokens = $flag;
    }

    /**
     * @return bool
     */
    public function getCacheTokens()
    {
        return $this->cacheTokens;
    }

    /**
     * @param bool $flag
     *
     * @throws InvalidArgumentException
     */
    public function setCheckForUnintentionallyCoveredCode($flag)
    {
        if (!is_bool($flag)) {
            throw InvalidArgumentException::create(
                1,
                'boolean'
            );
        }

        $this->checkForUnintentionallyCoveredCode = $flag;
    }

    /**
     * @param bool $flag
     *
     * @throws InvalidArgumentException
     */
    public function setForceCoversAnnotation($flag)
    {
        if (!is_bool($flag)) {
            throw InvalidArgumentException::create(
                1,
                'boolean'
            );
        }

        $this->forceCoversAnnotation = $flag;
    }

    /**
     * @param bool $flag
     *
     * @throws InvalidArgumentException
     */
    public function setCheckForMissingCoversAnnotation($flag)
    {
        if (!is_bool($flag)) {
            throw InvalidArgumentException::create(
                1,
                'boolean'
            );
        }

        $this->checkForMissingCoversAnnotation = $flag;
    }

    /**
     * @param bool $flag
     *
     * @throws InvalidArgumentException
     */
    public function setCheckForUnexecutedCoveredCode($flag)
    {
        if (!is_bool($flag)) {
            throw InvalidArgumentException::create(
                1,
                'boolean'
            );
        }

        $this->checkForUnexecutedCoveredCode = $flag;
    }

    /**
     * @deprecated
     *
     * @param bool $flag
     *
     * @throws InvalidArgumentException
     */
    public function setMapTestClassNameToCoveredClassName($flag)
    {
    }

    /**
     * @param bool $flag
     *
     * @throws InvalidArgumentException
     */
    public function setAddUncoveredFilesFromWhitelist($flag)
    {
        if (!is_bool($flag)) {
            throw InvalidArgumentException::create(
                1,
                'boolean'
            );
        }

        $this->addUncoveredFilesFromWhitelist = $flag;
    }

    /**
     * @param bool $flag
     *
     * @throws InvalidArgumentException
     */
    public function setProcessUncoveredFilesFromWhitelist($flag)
    {
        if (!is_bool($flag)) {
            throw InvalidArgumentException::create(
                1,
                'boolean'
            );
        }

        $this->processUncoveredFilesFromWhitelist = $flag;
    }

    /**
     * @param bool $flag
     *
     * @throws InvalidArgumentException
     */
    public function setDisableIgnoredLines($flag)
    {
        if (!is_bool($flag)) {
            throw InvalidArgumentException::create(
                1,
                'boolean'
            );
        }

        $this->disableIgnoredLines = $flag;
    }

    /**
     * @param bool $flag
     *
     * @throws InvalidArgumentException
     */
    public function setIgnoreDeprecatedCode($flag)
    {
        if (!is_bool($flag)) {
            throw InvalidArgumentException::create(
                1,
                'boolean'
            );
        }

        $this->ignoreDeprecatedCode = $flag;
    }

    /**
     * @param array $whitelist
     */
    public function setUnintentionallyCoveredSubclassesWhitelist(array $whitelist)
    {
        $this->unintentionallyCoveredSubclassesWhitelist = $whitelist;
    }

    /**
     * Applies the @covers annotation filtering.
     *
     * @param array $data
     * @param mixed $linesToBeCovered
     * @param array $linesToBeUsed
     *
     * @throws MissingCoversAnnotationException
     * @throws UnintentionallyCoveredCodeException
     */
    private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, array $linesToBeUsed)
    {
        if ($linesToBeCovered === false ||
            ($this->forceCoversAnnotation && empty($linesToBeCovered))) {
            if ($this->checkForMissingCoversAnnotation) {
                throw new MissingCoversAnnotationException;
            }

            $data = [];

            return;
        }

        if (empty($linesToBeCovered)) {
            return;
        }

        if ($this->checkForUnintentionallyCoveredCode &&
            (!$this->currentId instanceof \PHPUnit_Framework_TestCase ||
            (!$this->currentId->isMedium() && !$this->currentId->isLarge()))) {
            $this->performUnintentionallyCoveredCodeCheck(
                $data,
                $linesToBeCovered,
                $linesToBeUsed
            );
        }

        if ($this->checkForUnexecutedCoveredCode) {
            $this->performUnexecutedCoveredCodeCheck($data, $linesToBeCovered, $linesToBeUsed);
        }

        $data = array_intersect_key($data, $linesToBeCovered);

        foreach (array_keys($data) as $filename) {
            $_linesToBeCovered = array_flip($linesToBeCovered[$filename]);

            $data[$filename] = array_intersect_key(
                $data[$filename],
                $_linesToBeCovered
            );
        }
    }

    /**
     * Applies the whitelist filtering.
     *
     * @param array $data
     */
    private function applyListsFilter(array &$data)
    {
        foreach (array_keys($data) as $filename) {
            if ($this->filter->isFiltered($filename)) {
                unset($data[$filename]);
            }
        }
    }

    /**
     * Applies the "ignored lines" filtering.
     *
     * @param array $data
     */
    private function applyIgnoredLinesFilter(array &$data)
    {
        foreach (array_keys($data) as $filename) {
            if (!$this->filter->isFile($filename)) {
                continue;
            }

            foreach ($this->getLinesToBeIgnored($filename) as $line) {
                unset($data[$filename][$line]);
            }
        }
    }

    /**
     * @param array $data
     */
    private function initializeFilesThatAreSeenTheFirstTime(array $data)
    {
        foreach ($data as $file => $lines) {
            if ($this->filter->isFile($file) && !isset($this->data[$file])) {
                $this->data[$file] = [];

                foreach ($lines as $k => $v) {
                    $this->data[$file][$k] = $v == -2 ? null : [];
                }
            }
        }
    }

    /**
     * Processes whitelisted files that are not covered.
     */
    private function addUncoveredFilesFromWhitelist()
    {
        $data           = [];
        $uncoveredFiles = array_diff(
            $this->filter->getWhitelist(),
            array_keys($this->data)
        );

        foreach ($uncoveredFiles as $uncoveredFile) {
            if (!file_exists($uncoveredFile)) {
                continue;
            }

            if (!$this->processUncoveredFilesFromWhitelist) {
                $data[$uncoveredFile] = [];

                $lines = count(file($uncoveredFile));

                for ($i = 1; $i <= $lines; $i++) {
                    $data[$uncoveredFile][$i] = Driver::LINE_NOT_EXECUTED;
                }
            }
        }

        $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST');
    }

    /**
     * Returns the lines of a source file that should be ignored.
     *
     * @param string $filename
     *
     * @return array
     *
     * @throws InvalidArgumentException
     */
    private function getLinesToBeIgnored($filename)
    {
        if (!is_string($filename)) {
            throw InvalidArgumentException::create(
                1,
                'string'
            );
        }

        if (!isset($this->ignoredLines[$filename])) {
            $this->ignoredLines[$filename] = [];

            if ($this->disableIgnoredLines) {
                return $this->ignoredLines[$filename];
            }

            $ignore   = false;
            $stop     = false;
            $lines    = file($filename);
            $numLines = count($lines);

            foreach ($lines as $index => $line) {
                if (!trim($line)) {
                    $this->ignoredLines[$filename][] = $index + 1;
                }
            }

            if ($this->cacheTokens) {
                $tokens = \PHP_Token_Stream_CachingFactory::get($filename);
            } else {
                $tokens = new \PHP_Token_Stream($filename);
            }

            $classes = array_merge($tokens->getClasses(), $tokens->getTraits());
            $tokens  = $tokens->tokens();

            foreach ($tokens as $token) {
                switch (get_class($token)) {
                    case 'PHP_Token_COMMENT':
                    case 'PHP_Token_DOC_COMMENT':
                        $_token = trim($token);
                        $_line  = trim($lines[$token->getLine() - 1]);

                        if ($_token == '// @codeCoverageIgnore' ||
                            $_token == '//@codeCoverageIgnore') {
                            $ignore = true;
                            $stop   = true;
                        } elseif ($_token == '// @codeCoverageIgnoreStart' ||
                            $_token == '//@codeCoverageIgnoreStart') {
                            $ignore = true;
                        } elseif ($_token == '// @codeCoverageIgnoreEnd' ||
                            $_token == '//@codeCoverageIgnoreEnd') {
                            $stop = true;
                        }

                        if (!$ignore) {
                            $start = $token->getLine();
                            $end   = $start + substr_count($token, "\n");

                            // Do not ignore the first line when there is a token
                            // before the comment
                            if (0 !== strpos($_token, $_line)) {
                                $start++;
                            }

                            for ($i = $start; $i < $end; $i++) {
                                $this->ignoredLines[$filename][] = $i;
                            }

                            // A DOC_COMMENT token or a COMMENT token starting with "/*"
                            // does not contain the final \n character in its text
                            if (isset($lines[$i-1]) && 0 === strpos($_token, '/*') && '*/' === substr(trim($lines[$i-1]), -2)) {
                                $this->ignoredLines[$filename][] = $i;
                            }
                        }
                        break;

                    case 'PHP_Token_INTERFACE':
                    case 'PHP_Token_TRAIT':
                    case 'PHP_Token_CLASS':
                    case 'PHP_Token_FUNCTION':
                        /* @var \PHP_Token_Interface $token */

                        $docblock = $token->getDocblock();

                        $this->ignoredLines[$filename][] = $token->getLine();

                        if (strpos($docblock, '@codeCoverageIgnore') || ($this->ignoreDeprecatedCode && strpos($docblock, '@deprecated'))) {
                            $endLine = $token->getEndLine();

                            for ($i = $token->getLine(); $i <= $endLine; $i++) {
                                $this->ignoredLines[$filename][] = $i;
                            }
                        } elseif ($token instanceof \PHP_Token_INTERFACE ||
                            $token instanceof \PHP_Token_TRAIT ||
                            $token instanceof \PHP_Token_CLASS) {
                            if (empty($classes[$token->getName()]['methods'])) {
                                for ($i = $token->getLine();
                                     $i <= $token->getEndLine();
                                     $i++) {
                                    $this->ignoredLines[$filename][] = $i;
                                }
                            } else {
                                $firstMethod = array_shift(
                                    $classes[$token->getName()]['methods']
                                );

                                do {
                                    $lastMethod = array_pop(
                                        $classes[$token->getName()]['methods']
                                    );
                                } while ($lastMethod !== null &&
                                    substr($lastMethod['signature'], 0, 18) == 'anonymous function');

                                if ($lastMethod === null) {
                                    $lastMethod = $firstMethod;
                                }

                                for ($i = $token->getLine();
                                     $i < $firstMethod['startLine'];
                                     $i++) {
                                    $this->ignoredLines[$filename][] = $i;
                                }

                                for ($i = $token->getEndLine();
                                     $i > $lastMethod['endLine'];
                                     $i--) {
                                    $this->ignoredLines[$filename][] = $i;
                                }
                            }
                        }
                        break;

                    case 'PHP_Token_NAMESPACE':
                        $this->ignoredLines[$filename][] = $token->getEndLine();

                    // Intentional fallthrough
                    case 'PHP_Token_DECLARE':
                    case 'PHP_Token_OPEN_TAG':
                    case 'PHP_Token_CLOSE_TAG':
                    case 'PHP_Token_USE':
                        $this->ignoredLines[$filename][] = $token->getLine();
                        break;
                }

                if ($ignore) {
                    $this->ignoredLines[$filename][] = $token->getLine();

                    if ($stop) {
                        $ignore = false;
                        $stop   = false;
                    }
                }
            }

            $this->ignoredLines[$filename][] = $numLines + 1;

            $this->ignoredLines[$filename] = array_unique(
                $this->ignoredLines[$filename]
            );

            sort($this->ignoredLines[$filename]);
        }

        return $this->ignoredLines[$filename];
    }

    /**
     * @param array $data
     * @param array $linesToBeCovered
     * @param array $linesToBeUsed
     *
     * @throws UnintentionallyCoveredCodeException
     */
    private function performUnintentionallyCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed)
    {
        $allowedLines = $this->getAllowedLines(
            $linesToBeCovered,
            $linesToBeUsed
        );

        $unintentionallyCoveredUnits = [];

        foreach ($data as $file => $_data) {
            foreach ($_data as $line => $flag) {
                if ($flag == 1 && !isset($allowedLines[$file][$line])) {
                    $unintentionallyCoveredUnits[] = $this->wizard->lookup($file, $line);
                }
            }
        }

        $unintentionallyCoveredUnits = $this->processUnintentionallyCoveredUnits($unintentionallyCoveredUnits);

        if (!empty($unintentionallyCoveredUnits)) {
            throw new UnintentionallyCoveredCodeException(
                $unintentionallyCoveredUnits
            );
        }
    }

    /**
     * @param array $data
     * @param array $linesToBeCovered
     * @param array $linesToBeUsed
     *
     * @throws CoveredCodeNotExecutedException
     */
    private function performUnexecutedCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed)
    {
        $expectedLines = $this->getAllowedLines(
            $linesToBeCovered,
            $linesToBeUsed
        );

        foreach ($data as $file => $_data) {
            foreach (array_keys($_data) as $line) {
                if (!isset($expectedLines[$file][$line])) {
                    continue;
                }

                unset($expectedLines[$file][$line]);
            }
        }

        $message = '';

        foreach ($expectedLines as $file => $lines) {
            if (empty($lines)) {
                continue;
            }

            foreach (array_keys($lines) as $line) {
                $message .= sprintf('- %s:%d' . PHP_EOL, $file, $line);
            }
        }

        if (!empty($message)) {
            throw new CoveredCodeNotExecutedException($message);
        }
    }

    /**
     * @param array $linesToBeCovered
     * @param array $linesToBeUsed
     *
     * @return array
     */
    private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed)
    {
        $allowedLines = [];

        foreach (array_keys($linesToBeCovered) as $file) {
            if (!isset($allowedLines[$file])) {
                $allowedLines[$file] = [];
            }

            $allowedLines[$file] = array_merge(
                $allowedLines[$file],
                $linesToBeCovered[$file]
            );
        }

        foreach (array_keys($linesToBeUsed) as $file) {
            if (!isset($allowedLines[$file])) {
                $allowedLines[$file] = [];
            }

            $allowedLines[$file] = array_merge(
                $allowedLines[$file],
                $linesToBeUsed[$file]
            );
        }

        foreach (array_keys($allowedLines) as $file) {
            $allowedLines[$file] = array_flip(
                array_unique($allowedLines[$file])
            );
        }

        return $allowedLines;
    }

    /**
     * @return Driver
     *
     * @throws RuntimeException
     */
    private function selectDriver()
    {
        $runtime = new Runtime;

        if (!$runtime->canCollectCodeCoverage()) {
            throw new RuntimeException('No code coverage driver available');
        }

        if ($runtime->isHHVM()) {
            return new HHVM;
        } elseif ($runtime->isPHPDBG()) {
            return new PHPDBG;
        } else {
            return new Xdebug;
        }
    }

    /**
     * @param array $unintentionallyCoveredUnits
     *
     * @return array
     */
    private function processUnintentionallyCoveredUnits(array $unintentionallyCoveredUnits)
    {
        $unintentionallyCoveredUnits = array_unique($unintentionallyCoveredUnits);
        sort($unintentionallyCoveredUnits);

        foreach (array_keys($unintentionallyCoveredUnits) as $k => $v) {
            $unit = explode('::', $unintentionallyCoveredUnits[$k]);

            if (count($unit) != 2) {
                continue;
            }

            $class = new \ReflectionClass($unit[0]);

            foreach ($this->unintentionallyCoveredSubclassesWhitelist as $whitelisted) {
                if ($class->isSubclassOf($whitelisted)) {
                    unset($unintentionallyCoveredUnits[$k]);
                    break;
                }
            }
        }

        return array_values($unintentionallyCoveredUnits);
    }

    /**
     * If we are processing uncovered files from whitelist,
     * we can initialize the data before we start to speed up the tests
     */
    protected function initializeData()
    {
        $this->isInitialized = true;

        if ($this->processUncoveredFilesFromWhitelist) {
            $this->shouldCheckForDeadAndUnused = false;

            $this->driver->start(true);

            foreach ($this->filter->getWhitelist() as $file) {
                if ($this->filter->isFile($file)) {
                    include_once($file);
                }
            }

            $data     = [];
            $coverage = $this->driver->stop();

            foreach ($coverage as $file => $fileCoverage) {
                if ($this->filter->isFiltered($file)) {
                    continue;
                }

                foreach (array_keys($fileCoverage) as $key) {
                    if ($fileCoverage[$key] == Driver::LINE_EXECUTED) {
                        $fileCoverage[$key] = Driver::LINE_NOT_EXECUTED;
                    }
                }

                $data[$file] = $fileCoverage;
            }

            $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST');
        }
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Driver;

/**
 * Interface for code coverage drivers.
 */
interface Driver
{
    /**
     * @var int
     *
     * @see http://xdebug.org/docs/code_coverage
     */
    const LINE_EXECUTED = 1;

    /**
     * @var int
     *
     * @see http://xdebug.org/docs/code_coverage
     */
    const LINE_NOT_EXECUTED = -1;

    /**
     * @var int
     *
     * @see http://xdebug.org/docs/code_coverage
     */
    const LINE_NOT_EXECUTABLE = -2;

    /**
     * Start collection of code coverage information.
     *
     * @param bool $determineUnusedAndDead
     */
    public function start($determineUnusedAndDead = true);

    /**
     * Stop collection of code coverage information.
     *
     * @return array
     */
    public function stop();
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Driver;

/**
 * Driver for HHVM's code coverage functionality.
 *
 * @codeCoverageIgnore
 */
class HHVM extends Xdebug
{
    /**
     * Start collection of code coverage information.
     *
     * @param bool $determineUnusedAndDead
     */
    public function start($determineUnusedAndDead = true)
    {
        xdebug_start_code_coverage();
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Driver;

use SebastianBergmann\CodeCoverage\RuntimeException;

/**
 * Driver for PHPDBG's code coverage functionality.
 *
 * @codeCoverageIgnore
 */
class PHPDBG implements Driver
{
    /**
     * Constructor.
     */
    public function __construct()
    {
        if (PHP_SAPI !== 'phpdbg') {
            throw new RuntimeException(
                'This driver requires the PHPDBG SAPI'
            );
        }

        if (!function_exists('phpdbg_start_oplog')) {
            throw new RuntimeException(
                'This build of PHPDBG does not support code coverage'
            );
        }
    }

    /**
     * Start collection of code coverage information.
     *
     * @param bool $determineUnusedAndDead
     */
    public function start($determineUnusedAndDead = true)
    {
        phpdbg_start_oplog();
    }

    /**
     * Stop collection of code coverage information.
     *
     * @return array
     */
    public function stop()
    {
        static $fetchedLines = [];

        $dbgData = phpdbg_end_oplog();

        if ($fetchedLines == []) {
            $sourceLines = phpdbg_get_executable();
        } else {
            $newFiles = array_diff(
                get_included_files(),
                array_keys($fetchedLines)
            );

            if ($newFiles) {
                $sourceLines = phpdbg_get_executable(
                    ['files' => $newFiles]
                );
            } else {
                $sourceLines = [];
            }
        }

        foreach ($sourceLines as $file => $lines) {
            foreach ($lines as $lineNo => $numExecuted) {
                $sourceLines[$file][$lineNo] = self::LINE_NOT_EXECUTED;
            }
        }

        $fetchedLines = array_merge($fetchedLines, $sourceLines);

        return $this->detectExecutedLines($fetchedLines, $dbgData);
    }

    /**
     * Convert phpdbg based data into the format CodeCoverage expects
     *
     * @param array $sourceLines
     * @param array $dbgData
     *
     * @return array
     */
    private function detectExecutedLines(array $sourceLines, array $dbgData)
    {
        foreach ($dbgData as $file => $coveredLines) {
            foreach ($coveredLines as $lineNo => $numExecuted) {
                // phpdbg also reports $lineNo=0 when e.g. exceptions get thrown.
                // make sure we only mark lines executed which are actually executable.
                if (isset($sourceLines[$file][$lineNo])) {
                    $sourceLines[$file][$lineNo] = self::LINE_EXECUTED;
                }
            }
        }

        return $sourceLines;
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Driver;

use SebastianBergmann\CodeCoverage\RuntimeException;

/**
 * Driver for Xdebug's code coverage functionality.
 *
 * @codeCoverageIgnore
 */
class Xdebug implements Driver
{
    /**
     * Cache the number of lines for each file
     *
     * @var array
     */
    private $cacheNumLines = [];

    /**
     * Constructor.
     */
    public function __construct()
    {
        if (!extension_loaded('xdebug')) {
            throw new RuntimeException('This driver requires Xdebug');
        }

        if (version_compare(phpversion('xdebug'), '2.2.1', '>=') &&
            !ini_get('xdebug.coverage_enable')) {
            throw new RuntimeException(
                'xdebug.coverage_enable=On has to be set in php.ini'
            );
        }
    }

    /**
     * Start collection of code coverage information.
     *
     * @param bool $determineUnusedAndDead
     */
    public function start($determineUnusedAndDead = true)
    {
        if ($determineUnusedAndDead) {
            xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
        } else {
            xdebug_start_code_coverage();
        }
    }

    /**
     * Stop collection of code coverage information.
     *
     * @return array
     */
    public function stop()
    {
        $data = xdebug_get_code_coverage();
        xdebug_stop_code_coverage();

        return $this->cleanup($data);
    }

    /**
     * @param array $data
     *
     * @return array
     */
    private function cleanup(array $data)
    {
        foreach (array_keys($data) as $file) {
            unset($data[$file][0]);

            if (strpos($file, 'xdebug://debug-eval') !== 0 && file_exists($file)) {
                $numLines = $this->getNumberOfLinesInFile($file);

                foreach (array_keys($data[$file]) as $line) {
                    if ($line > $numLines) {
                        unset($data[$file][$line]);
                    }
                }
            }
        }

        return $data;
    }

    /**
     * @param string $file
     *
     * @return int
     */
    private function getNumberOfLinesInFile($file)
    {
        if (!isset($this->cacheNumLines[$file])) {
            $buffer = file_get_contents($file);
            $lines  = substr_count($buffer, "\n");

            if (substr($buffer, -1) !== "\n") {
                $lines++;
            }

            $this->cacheNumLines[$file] = $lines;
        }

        return $this->cacheNumLines[$file];
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage;

/**
 * Exception that is raised when covered code is not executed.
 */
class CoveredCodeNotExecutedException extends RuntimeException
{
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage;

/**
 * Exception interface for php-code-coverage component.
 */
interface Exception
{
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage;

class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
    /**
     * @param int    $argument
     * @param string $type
     * @param mixed  $value
     *
     * @return InvalidArgumentException
     */
    public static function create($argument, $type, $value = null)
    {
        $stack = debug_backtrace(0);

        return new self(
            sprintf(
                'Argument #%d%sof %s::%s() must be a %s',
                $argument,
                $value !== null ? ' (' . gettype($value) . '#' . $value . ')' : ' (No Value) ',
                $stack[1]['class'],
                $stack[1]['function'],
                $type
            )
        );
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage;

/**
 * Exception that is raised when @covers must be used but is not.
 */
class MissingCoversAnnotationException extends RuntimeException
{
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage;

class RuntimeException extends \RuntimeException implements Exception
{
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage;

/**
 * Exception that is raised when code is unintentionally covered.
 */
class UnintentionallyCoveredCodeException extends RuntimeException
{
    /**
     * @var array
     */
    private $unintentionallyCoveredUnits = [];

    /**
     * @param array $unintentionallyCoveredUnits
     */
    public function __construct(array $unintentionallyCoveredUnits)
    {
        $this->unintentionallyCoveredUnits = $unintentionallyCoveredUnits;

        parent::__construct($this->toString());
    }

    /**
     * @return array
     */
    public function getUnintentionallyCoveredUnits()
    {
        return $this->unintentionallyCoveredUnits;
    }

    /**
     * @return string
     */
    private function toString()
    {
        $message = '';

        foreach ($this->unintentionallyCoveredUnits as $unit) {
            $message .= '- ' . $unit . "\n";
        }

        return $message;
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage;

/**
 * Filter for whitelisting of code coverage information.
 */
class Filter
{
    /**
     * Source files that are whitelisted.
     *
     * @var array
     */
    private $whitelistedFiles = [];

    /**
     * Adds a directory to the whitelist (recursively).
     *
     * @param string $directory
     * @param string $suffix
     * @param string $prefix
     */
    public function addDirectoryToWhitelist($directory, $suffix = '.php', $prefix = '')
    {
        $facade = new \File_Iterator_Facade;
        $files  = $facade->getFilesAsArray($directory, $suffix, $prefix);

        foreach ($files as $file) {
            $this->addFileToWhitelist($file);
        }
    }

    /**
     * Adds a file to the whitelist.
     *
     * @param string $filename
     */
    public function addFileToWhitelist($filename)
    {
        $this->whitelistedFiles[realpath($filename)] = true;
    }

    /**
     * Adds files to the whitelist.
     *
     * @param array $files
     */
    public function addFilesToWhitelist(array $files)
    {
        foreach ($files as $file) {
            $this->addFileToWhitelist($file);
        }
    }

    /**
     * Removes a directory from the whitelist (recursively).
     *
     * @param string $directory
     * @param string $suffix
     * @param string $prefix
     */
    public function removeDirectoryFromWhitelist($directory, $suffix = '.php', $prefix = '')
    {
        $facade = new \File_Iterator_Facade;
        $files  = $facade->getFilesAsArray($directory, $suffix, $prefix);

        foreach ($files as $file) {
            $this->removeFileFromWhitelist($file);
        }
    }

    /**
     * Removes a file from the whitelist.
     *
     * @param string $filename
     */
    public function removeFileFromWhitelist($filename)
    {
        $filename = realpath($filename);

        unset($this->whitelistedFiles[$filename]);
    }

    /**
     * Checks whether a filename is a real filename.
     *
     * @param string $filename
     *
     * @return bool
     */
    public function isFile($filename)
    {
        if ($filename == '-' ||
            strpos($filename, 'vfs://') === 0 ||
            strpos($filename, 'xdebug://debug-eval') !== false ||
            strpos($filename, 'eval()\'d code') !== false ||
            strpos($filename, 'runtime-created function') !== false ||
            strpos($filename, 'runkit created function') !== false ||
            strpos($filename, 'assert code') !== false ||
            strpos($filename, 'regexp code') !== false) {
            return false;
        }

        return file_exists($filename);
    }

    /**
     * Checks whether or not a file is filtered.
     *
     * @param string $filename
     *
     * @return bool
     */
    public function isFiltered($filename)
    {
        if (!$this->isFile($filename)) {
            return true;
        }

        $filename = realpath($filename);

        return !isset($this->whitelistedFiles[$filename]);
    }

    /**
     * Returns the list of whitelisted files.
     *
     * @return array
     */
    public function getWhitelist()
    {
        return array_keys($this->whitelistedFiles);
    }

    /**
     * Returns whether this filter has a whitelist.
     *
     * @return bool
     */
    public function hasWhitelist()
    {
        return !empty($this->whitelistedFiles);
    }

    /**
     * Returns the whitelisted files.
     *
     * @return array
     */
    public function getWhitelistedFiles()
    {
        return $this->whitelistedFiles;
    }

    /**
     * Sets the whitelisted files.
     *
     * @param array $whitelistedFiles
     */
    public function setWhitelistedFiles($whitelistedFiles)
    {
        $this->whitelistedFiles = $whitelistedFiles;
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Node;

use SebastianBergmann\CodeCoverage\Util;

/**
 * Base class for nodes in the code coverage information tree.
 */
abstract class AbstractNode implements \Countable
{
    /**
     * @var string
     */
    private $name;

    /**
     * @var string
     */
    private $path;

    /**
     * @var array
     */
    private $pathArray;

    /**
     * @var AbstractNode
     */
    private $parent;

    /**
     * @var string
     */
    private $id;

    /**
     * Constructor.
     *
     * @param string       $name
     * @param AbstractNode $parent
     */
    public function __construct($name, AbstractNode $parent = null)
    {
        if (substr($name, -1) == '/') {
            $name = substr($name, 0, -1);
        }

        $this->name   = $name;
        $this->parent = $parent;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @return string
     */
    public function getId()
    {
        if ($this->id === null) {
            $parent = $this->getParent();

            if ($parent === null) {
                $this->id = 'index';
            } else {
                $parentId = $parent->getId();

                if ($parentId == 'index') {
                    $this->id = str_replace(':', '_', $this->name);
                } else {
                    $this->id = $parentId . '/' . $this->name;
                }
            }
        }

        return $this->id;
    }

    /**
     * @return string
     */
    public function getPath()
    {
        if ($this->path === null) {
            if ($this->parent === null || $this->parent->getPath() === null || $this->parent->getPath() === false) {
                $this->path = $this->name;
            } else {
                $this->path = $this->parent->getPath() . '/' . $this->name;
            }
        }

        return $this->path;
    }

    /**
     * @return array
     */
    public function getPathAsArray()
    {
        if ($this->pathArray === null) {
            if ($this->parent === null) {
                $this->pathArray = [];
            } else {
                $this->pathArray = $this->parent->getPathAsArray();
            }

            $this->pathArray[] = $this;
        }

        return $this->pathArray;
    }

    /**
     * @return AbstractNode
     */
    public function getParent()
    {
        return $this->parent;
    }

    /**
     * Returns the percentage of classes that has been tested.
     *
     * @param bool $asString
     *
     * @return int
     */
    public function getTestedClassesPercent($asString = true)
    {
        return Util::percent(
            $this->getNumTestedClasses(),
            $this->getNumClasses(),
            $asString
        );
    }

    /**
     * Returns the percentage of traits that has been tested.
     *
     * @param bool $asString
     *
     * @return int
     */
    public function getTestedTraitsPercent($asString = true)
    {
        return Util::percent(
            $this->getNumTestedTraits(),
            $this->getNumTraits(),
            $asString
        );
    }

    /**
     * Returns the percentage of traits that has been tested.
     *
     * @param bool $asString
     *
     * @return int
     */
    public function getTestedClassesAndTraitsPercent($asString = true)
    {
        return Util::percent(
            $this->getNumTestedClassesAndTraits(),
            $this->getNumClassesAndTraits(),
            $asString
        );
    }

    /**
     * Returns the percentage of methods that has been tested.
     *
     * @param bool $asString
     *
     * @return int
     */
    public function getTestedMethodsPercent($asString = true)
    {
        return Util::percent(
            $this->getNumTestedMethods(),
            $this->getNumMethods(),
            $asString
        );
    }

    /**
     * Returns the percentage of executed lines.
     *
     * @param bool $asString
     *
     * @return int
     */
    public function getLineExecutedPercent($asString = true)
    {
        return Util::percent(
            $this->getNumExecutedLines(),
            $this->getNumExecutableLines(),
            $asString
        );
    }

    /**
     * Returns the number of classes and traits.
     *
     * @return int
     */
    public function getNumClassesAndTraits()
    {
        return $this->getNumClasses() + $this->getNumTraits();
    }

    /**
     * Returns the number of tested classes and traits.
     *
     * @return int
     */
    public function getNumTestedClassesAndTraits()
    {
        return $this->getNumTestedClasses() + $this->getNumTestedTraits();
    }

    /**
     * Returns the classes and traits of this node.
     *
     * @return array
     */
    public function getClassesAndTraits()
    {
        return array_merge($this->getClasses(), $this->getTraits());
    }

    /**
     * Returns the classes of this node.
     *
     * @return array
     */
    abstract public function getClasses();

    /**
     * Returns the traits of this node.
     *
     * @return array
     */
    abstract public function getTraits();

    /**
     * Returns the functions of this node.
     *
     * @return array
     */
    abstract public function getFunctions();

    /**
     * Returns the LOC/CLOC/NCLOC of this node.
     *
     * @return array
     */
    abstract public function getLinesOfCode();

    /**
     * Returns the number of executable lines.
     *
     * @return int
     */
    abstract public function getNumExecutableLines();

    /**
     * Returns the number of executed lines.
     *
     * @return int
     */
    abstract public function getNumExecutedLines();

    /**
     * Returns the number of classes.
     *
     * @return int
     */
    abstract public function getNumClasses();

    /**
     * Returns the number of tested classes.
     *
     * @return int
     */
    abstract public function getNumTestedClasses();

    /**
     * Returns the number of traits.
     *
     * @return int
     */
    abstract public function getNumTraits();

    /**
     * Returns the number of tested traits.
     *
     * @return int
     */
    abstract public function getNumTestedTraits();

    /**
     * Returns the number of methods.
     *
     * @return int
     */
    abstract public function getNumMethods();

    /**
     * Returns the number of tested methods.
     *
     * @return int
     */
    abstract public function getNumTestedMethods();

    /**
     * Returns the number of functions.
     *
     * @return int
     */
    abstract public function getNumFunctions();

    /**
     * Returns the number of tested functions.
     *
     * @return int
     */
    abstract public function getNumTestedFunctions();
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Node;

use SebastianBergmann\CodeCoverage\CodeCoverage;

class Builder
{
    /**
     * @param CodeCoverage $coverage
     *
     * @return Directory
     */
    public function build(CodeCoverage $coverage)
    {
        $files      = $coverage->getData();
        $commonPath = $this->reducePaths($files);
        $root       = new Directory(
            $commonPath,
            null
        );

        $this->addItems(
            $root,
            $this->buildDirectoryStructure($files),
            $coverage->getTests(),
            $coverage->getCacheTokens()
        );

        return $root;
    }

    /**
     * @param Directory $root
     * @param array     $items
     * @param array     $tests
     * @param bool      $cacheTokens
     */
    private function addItems(Directory $root, array $items, array $tests, $cacheTokens)
    {
        foreach ($items as $key => $value) {
            if (substr($key, -2) == '/f') {
                $key = substr($key, 0, -2);

                if (file_exists($root->getPath() . DIRECTORY_SEPARATOR . $key)) {
                    $root->addFile($key, $value, $tests, $cacheTokens);
                }
            } else {
                $child = $root->addDirectory($key);
                $this->addItems($child, $value, $tests, $cacheTokens);
            }
        }
    }

    /**
     * Builds an array representation of the directory structure.
     *
     * For instance,
     *
     * <code>
     * Array
     * (
     *     [Money.php] => Array
     *         (
     *             ...
     *         )
     *
     *     [MoneyBag.php] => Array
     *         (
     *             ...
     *         )
     * )
     * </code>
     *
     * is transformed into
     *
     * <code>
     * Array
     * (
     *     [.] => Array
     *         (
     *             [Money.php] => Array
     *                 (
     *                     ...
     *                 )
     *
     *             [MoneyBag.php] => Array
     *                 (
     *                     ...
     *                 )
     *         )
     * )
     * </code>
     *
     * @param array $files
     *
     * @return array
     */
    private function buildDirectoryStructure($files)
    {
        $result = [];

        foreach ($files as $path => $file) {
            $path    = explode('/', $path);
            $pointer = &$result;
            $max     = count($path);

            for ($i = 0; $i < $max; $i++) {
                if ($i == ($max - 1)) {
                    $type = '/f';
                } else {
                    $type = '';
                }

                $pointer = &$pointer[$path[$i] . $type];
            }

            $pointer = $file;
        }

        return $result;
    }

    /**
     * Reduces the paths by cutting the longest common start path.
     *
     * For instance,
     *
     * <code>
     * Array
     * (
     *     [/home/sb/Money/Money.php] => Array
     *         (
     *             ...
     *         )
     *
     *     [/home/sb/Money/MoneyBag.php] => Array
     *         (
     *             ...
     *         )
     * )
     * </code>
     *
     * is reduced to
     *
     * <code>
     * Array
     * (
     *     [Money.php] => Array
     *         (
     *             ...
     *         )
     *
     *     [MoneyBag.php] => Array
     *         (
     *             ...
     *         )
     * )
     * </code>
     *
     * @param array $files
     *
     * @return string
     */
    private function reducePaths(&$files)
    {
        if (empty($files)) {
            return '.';
        }

        $commonPath = '';
        $paths      = array_keys($files);

        if (count($files) == 1) {
            $commonPath                 = dirname($paths[0]) . '/';
            $files[basename($paths[0])] = $files[$paths[0]];

            unset($files[$paths[0]]);

            return $commonPath;
        }

        $max = count($paths);

        for ($i = 0; $i < $max; $i++) {
            // strip phar:// prefixes
            if (strpos($paths[$i], 'phar://') === 0) {
                $paths[$i] = substr($paths[$i], 7);
                $paths[$i] = strtr($paths[$i], '/', DIRECTORY_SEPARATOR);
            }
            $paths[$i] = explode(DIRECTORY_SEPARATOR, $paths[$i]);

            if (empty($paths[$i][0])) {
                $paths[$i][0] = DIRECTORY_SEPARATOR;
            }
        }

        $done = false;
        $max  = count($paths);

        while (!$done) {
            for ($i = 0; $i < $max - 1; $i++) {
                if (!isset($paths[$i][0]) ||
                    !isset($paths[$i+1][0]) ||
                    $paths[$i][0] != $paths[$i+1][0]) {
                    $done = true;
                    break;
                }
            }

            if (!$done) {
                $commonPath .= $paths[0][0];

                if ($paths[0][0] != DIRECTORY_SEPARATOR) {
                    $commonPath .= DIRECTORY_SEPARATOR;
                }

                for ($i = 0; $i < $max; $i++) {
                    array_shift($paths[$i]);
                }
            }
        }

        $original = array_keys($files);
        $max      = count($original);

        for ($i = 0; $i < $max; $i++) {
            $files[implode('/', $paths[$i])] = $files[$original[$i]];
            unset($files[$original[$i]]);
        }

        ksort($files);

        return substr($commonPath, 0, -1);
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Node;

use SebastianBergmann\CodeCoverage\InvalidArgumentException;

/**
 * Represents a directory in the code coverage information tree.
 */
class Directory extends AbstractNode implements \IteratorAggregate
{
    /**
     * @var AbstractNode[]
     */
    private $children = [];

    /**
     * @var Directory[]
     */
    private $directories = [];

    /**
     * @var File[]
     */
    private $files = [];

    /**
     * @var array
     */
    private $classes;

    /**
     * @var array
     */
    private $traits;

    /**
     * @var array
     */
    private $functions;

    /**
     * @var array
     */
    private $linesOfCode = null;

    /**
     * @var int
     */
    private $numFiles = -1;

    /**
     * @var int
     */
    private $numExecutableLines = -1;

    /**
     * @var int
     */
    private $numExecutedLines = -1;

    /**
     * @var int
     */
    private $numClasses = -1;

    /**
     * @var int
     */
    private $numTestedClasses = -1;

    /**
     * @var int
     */
    private $numTraits = -1;

    /**
     * @var int
     */
    private $numTestedTraits = -1;

    /**
     * @var int
     */
    private $numMethods = -1;

    /**
     * @var int
     */
    private $numTestedMethods = -1;

    /**
     * @var int
     */
    private $numFunctions = -1;

    /**
     * @var int
     */
    private $numTestedFunctions = -1;

    /**
     * Returns the number of files in/under this node.
     *
     * @return int
     */
    public function count()
    {
        if ($this->numFiles == -1) {
            $this->numFiles = 0;

            foreach ($this->children as $child) {
                $this->numFiles += count($child);
            }
        }

        return $this->numFiles;
    }

    /**
     * Returns an iterator for this node.
     *
     * @return \RecursiveIteratorIterator
     */
    public function getIterator()
    {
        return new \RecursiveIteratorIterator(
            new Iterator($this),
            \RecursiveIteratorIterator::SELF_FIRST
        );
    }

    /**
     * Adds a new directory.
     *
     * @param string $name
     *
     * @return Directory
     */
    public function addDirectory($name)
    {
        $directory = new self($name, $this);

        $this->children[]    = $directory;
        $this->directories[] = &$this->children[count($this->children) - 1];

        return $directory;
    }

    /**
     * Adds a new file.
     *
     * @param string $name
     * @param array  $coverageData
     * @param array  $testData
     * @param bool   $cacheTokens
     *
     * @return File
     *
     * @throws InvalidArgumentException
     */
    public function addFile($name, array $coverageData, array $testData, $cacheTokens)
    {
        $file = new File(
            $name,
            $this,
            $coverageData,
            $testData,
            $cacheTokens
        );

        $this->children[] = $file;
        $this->files[]    = &$this->children[count($this->children) - 1];

        $this->numExecutableLines = -1;
        $this->numExecutedLines   = -1;

        return $file;
    }

    /**
     * Returns the directories in this directory.
     *
     * @return array
     */
    public function getDirectories()
    {
        return $this->directories;
    }

    /**
     * Returns the files in this directory.
     *
     * @return array
     */
    public function getFiles()
    {
        return $this->files;
    }

    /**
     * Returns the child nodes of this node.
     *
     * @return array
     */
    public function getChildNodes()
    {
        return $this->children;
    }

    /**
     * Returns the classes of this node.
     *
     * @return array
     */
    public function getClasses()
    {
        if ($this->classes === null) {
            $this->classes = [];

            foreach ($this->children as $child) {
                $this->classes = array_merge(
                    $this->classes,
                    $child->getClasses()
                );
            }
        }

        return $this->classes;
    }

    /**
     * Returns the traits of this node.
     *
     * @return array
     */
    public function getTraits()
    {
        if ($this->traits === null) {
            $this->traits = [];

            foreach ($this->children as $child) {
                $this->traits = array_merge(
                    $this->traits,
                    $child->getTraits()
                );
            }
        }

        return $this->traits;
    }

    /**
     * Returns the functions of this node.
     *
     * @return array
     */
    public function getFunctions()
    {
        if ($this->functions === null) {
            $this->functions = [];

            foreach ($this->children as $child) {
                $this->functions = array_merge(
                    $this->functions,
                    $child->getFunctions()
                );
            }
        }

        return $this->functions;
    }

    /**
     * Returns the LOC/CLOC/NCLOC of this node.
     *
     * @return array
     */
    public function getLinesOfCode()
    {
        if ($this->linesOfCode === null) {
            $this->linesOfCode = ['loc' => 0, 'cloc' => 0, 'ncloc' => 0];

            foreach ($this->children as $child) {
                $linesOfCode = $child->getLinesOfCode();

                $this->linesOfCode['loc']   += $linesOfCode['loc'];
                $this->linesOfCode['cloc']  += $linesOfCode['cloc'];
                $this->linesOfCode['ncloc'] += $linesOfCode['ncloc'];
            }
        }

        return $this->linesOfCode;
    }

    /**
     * Returns the number of executable lines.
     *
     * @return int
     */
    public function getNumExecutableLines()
    {
        if ($this->numExecutableLines == -1) {
            $this->numExecutableLines = 0;

            foreach ($this->children as $child) {
                $this->numExecutableLines += $child->getNumExecutableLines();
            }
        }

        return $this->numExecutableLines;
    }

    /**
     * Returns the number of executed lines.
     *
     * @return int
     */
    public function getNumExecutedLines()
    {
        if ($this->numExecutedLines == -1) {
            $this->numExecutedLines = 0;

            foreach ($this->children as $child) {
                $this->numExecutedLines += $child->getNumExecutedLines();
            }
        }

        return $this->numExecutedLines;
    }

    /**
     * Returns the number of classes.
     *
     * @return int
     */
    public function getNumClasses()
    {
        if ($this->numClasses == -1) {
            $this->numClasses = 0;

            foreach ($this->children as $child) {
                $this->numClasses += $child->getNumClasses();
            }
        }

        return $this->numClasses;
    }

    /**
     * Returns the number of tested classes.
     *
     * @return int
     */
    public function getNumTestedClasses()
    {
        if ($this->numTestedClasses == -1) {
            $this->numTestedClasses = 0;

            foreach ($this->children as $child) {
                $this->numTestedClasses += $child->getNumTestedClasses();
            }
        }

        return $this->numTestedClasses;
    }

    /**
     * Returns the number of traits.
     *
     * @return int
     */
    public function getNumTraits()
    {
        if ($this->numTraits == -1) {
            $this->numTraits = 0;

            foreach ($this->children as $child) {
                $this->numTraits += $child->getNumTraits();
            }
        }

        return $this->numTraits;
    }

    /**
     * Returns the number of tested traits.
     *
     * @return int
     */
    public function getNumTestedTraits()
    {
        if ($this->numTestedTraits == -1) {
            $this->numTestedTraits = 0;

            foreach ($this->children as $child) {
                $this->numTestedTraits += $child->getNumTestedTraits();
            }
        }

        return $this->numTestedTraits;
    }

    /**
     * Returns the number of methods.
     *
     * @return int
     */
    public function getNumMethods()
    {
        if ($this->numMethods == -1) {
            $this->numMethods = 0;

            foreach ($this->children as $child) {
                $this->numMethods += $child->getNumMethods();
            }
        }

        return $this->numMethods;
    }

    /**
     * Returns the number of tested methods.
     *
     * @return int
     */
    public function getNumTestedMethods()
    {
        if ($this->numTestedMethods == -1) {
            $this->numTestedMethods = 0;

            foreach ($this->children as $child) {
                $this->numTestedMethods += $child->getNumTestedMethods();
            }
        }

        return $this->numTestedMethods;
    }

    /**
     * Returns the number of functions.
     *
     * @return int
     */
    public function getNumFunctions()
    {
        if ($this->numFunctions == -1) {
            $this->numFunctions = 0;

            foreach ($this->children as $child) {
                $this->numFunctions += $child->getNumFunctions();
            }
        }

        return $this->numFunctions;
    }

    /**
     * Returns the number of tested functions.
     *
     * @return int
     */
    public function getNumTestedFunctions()
    {
        if ($this->numTestedFunctions == -1) {
            $this->numTestedFunctions = 0;

            foreach ($this->children as $child) {
                $this->numTestedFunctions += $child->getNumTestedFunctions();
            }
        }

        return $this->numTestedFunctions;
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Node;

use SebastianBergmann\CodeCoverage\InvalidArgumentException;

/**
 * Represents a file in the code coverage information tree.
 */
class File extends AbstractNode
{
    /**
     * @var array
     */
    private $coverageData;

    /**
     * @var array
     */
    private $testData;

    /**
     * @var int
     */
    private $numExecutableLines = 0;

    /**
     * @var int
     */
    private $numExecutedLines = 0;

    /**
     * @var array
     */
    private $classes = [];

    /**
     * @var array
     */
    private $traits = [];

    /**
     * @var array
     */
    private $functions = [];

    /**
     * @var array
     */
    private $linesOfCode = [];

    /**
     * @var int
     */
    private $numClasses = null;

    /**
     * @var int
     */
    private $numTestedClasses = 0;

    /**
     * @var int
     */
    private $numTraits = null;

    /**
     * @var int
     */
    private $numTestedTraits = 0;

    /**
     * @var int
     */
    private $numMethods = null;

    /**
     * @var int
     */
    private $numTestedMethods = null;

    /**
     * @var int
     */
    private $numTestedFunctions = null;

    /**
     * @var array
     */
    private $startLines = [];

    /**
     * @var array
     */
    private $endLines = [];

    /**
     * @var bool
     */
    private $cacheTokens;

    /**
     * Constructor.
     *
     * @param string       $name
     * @param AbstractNode $parent
     * @param array        $coverageData
     * @param array        $testData
     * @param bool         $cacheTokens
     *
     * @throws InvalidArgumentException
     */
    public function __construct($name, AbstractNode $parent, array $coverageData, array $testData, $cacheTokens)
    {
        if (!is_bool($cacheTokens)) {
            throw InvalidArgumentException::create(
                1,
                'boolean'
            );
        }

        parent::__construct($name, $parent);

        $this->coverageData = $coverageData;
        $this->testData     = $testData;
        $this->cacheTokens  = $cacheTokens;

        $this->calculateStatistics();
    }

    /**
     * Returns the number of files in/under this node.
     *
     * @return int
     */
    public function count()
    {
        return 1;
    }

    /**
     * Returns the code coverage data of this node.
     *
     * @return array
     */
    public function getCoverageData()
    {
        return $this->coverageData;
    }

    /**
     * Returns the test data of this node.
     *
     * @return array
     */
    public function getTestData()
    {
        return $this->testData;
    }

    /**
     * Returns the classes of this node.
     *
     * @return array
     */
    public function getClasses()
    {
        return $this->classes;
    }

    /**
     * Returns the traits of this node.
     *
     * @return array
     */
    public function getTraits()
    {
        return $this->traits;
    }

    /**
     * Returns the functions of this node.
     *
     * @return array
     */
    public function getFunctions()
    {
        return $this->functions;
    }

    /**
     * Returns the LOC/CLOC/NCLOC of this node.
     *
     * @return array
     */
    public function getLinesOfCode()
    {
        return $this->linesOfCode;
    }

    /**
     * Returns the number of executable lines.
     *
     * @return int
     */
    public function getNumExecutableLines()
    {
        return $this->numExecutableLines;
    }

    /**
     * Returns the number of executed lines.
     *
     * @return int
     */
    public function getNumExecutedLines()
    {
        return $this->numExecutedLines;
    }

    /**
     * Returns the number of classes.
     *
     * @return int
     */
    public function getNumClasses()
    {
        if ($this->numClasses === null) {
            $this->numClasses = 0;

            foreach ($this->classes as $class) {
                foreach ($class['methods'] as $method) {
                    if ($method['executableLines'] > 0) {
                        $this->numClasses++;

                        continue 2;
                    }
                }
            }
        }

        return $this->numClasses;
    }

    /**
     * Returns the number of tested classes.
     *
     * @return int
     */
    public function getNumTestedClasses()
    {
        return $this->numTestedClasses;
    }

    /**
     * Returns the number of traits.
     *
     * @return int
     */
    public function getNumTraits()
    {
        if ($this->numTraits === null) {
            $this->numTraits = 0;

            foreach ($this->traits as $trait) {
                foreach ($trait['methods'] as $method) {
                    if ($method['executableLines'] > 0) {
                        $this->numTraits++;

                        continue 2;
                    }
                }
            }
        }

        return $this->numTraits;
    }

    /**
     * Returns the number of tested traits.
     *
     * @return int
     */
    public function getNumTestedTraits()
    {
        return $this->numTestedTraits;
    }

    /**
     * Returns the number of methods.
     *
     * @return int
     */
    public function getNumMethods()
    {
        if ($this->numMethods === null) {
            $this->numMethods = 0;

            foreach ($this->classes as $class) {
                foreach ($class['methods'] as $method) {
                    if ($method['executableLines'] > 0) {
                        $this->numMethods++;
                    }
                }
            }

            foreach ($this->traits as $trait) {
                foreach ($trait['methods'] as $method) {
                    if ($method['executableLines'] > 0) {
                        $this->numMethods++;
                    }
                }
            }
        }

        return $this->numMethods;
    }

    /**
     * Returns the number of tested methods.
     *
     * @return int
     */
    public function getNumTestedMethods()
    {
        if ($this->numTestedMethods === null) {
            $this->numTestedMethods = 0;

            foreach ($this->classes as $class) {
                foreach ($class['methods'] as $method) {
                    if ($method['executableLines'] > 0 &&
                        $method['coverage'] == 100) {
                        $this->numTestedMethods++;
                    }
                }
            }

            foreach ($this->traits as $trait) {
                foreach ($trait['methods'] as $method) {
                    if ($method['executableLines'] > 0 &&
                        $method['coverage'] == 100) {
                        $this->numTestedMethods++;
                    }
                }
            }
        }

        return $this->numTestedMethods;
    }

    /**
     * Returns the number of functions.
     *
     * @return int
     */
    public function getNumFunctions()
    {
        return count($this->functions);
    }

    /**
     * Returns the number of tested functions.
     *
     * @return int
     */
    public function getNumTestedFunctions()
    {
        if ($this->numTestedFunctions === null) {
            $this->numTestedFunctions = 0;

            foreach ($this->functions as $function) {
                if ($function['executableLines'] > 0 &&
                    $function['coverage'] == 100) {
                    $this->numTestedFunctions++;
                }
            }
        }

        return $this->numTestedFunctions;
    }

    /**
     * Calculates coverage statistics for the file.
     */
    protected function calculateStatistics()
    {
        $classStack = $functionStack = [];

        if ($this->cacheTokens) {
            $tokens = \PHP_Token_Stream_CachingFactory::get($this->getPath());
        } else {
            $tokens = new \PHP_Token_Stream($this->getPath());
        }

        $this->processClasses($tokens);
        $this->processTraits($tokens);
        $this->processFunctions($tokens);
        $this->linesOfCode = $tokens->getLinesOfCode();
        unset($tokens);

        for ($lineNumber = 1; $lineNumber <= $this->linesOfCode['loc']; $lineNumber++) {
            if (isset($this->startLines[$lineNumber])) {
                // Start line of a class.
                if (isset($this->startLines[$lineNumber]['className'])) {
                    if (isset($currentClass)) {
                        $classStack[] = &$currentClass;
                    }

                    $currentClass = &$this->startLines[$lineNumber];
                } // Start line of a trait.
                elseif (isset($this->startLines[$lineNumber]['traitName'])) {
                    $currentTrait = &$this->startLines[$lineNumber];
                } // Start line of a method.
                elseif (isset($this->startLines[$lineNumber]['methodName'])) {
                    $currentMethod = &$this->startLines[$lineNumber];
                } // Start line of a function.
                elseif (isset($this->startLines[$lineNumber]['functionName'])) {
                    if (isset($currentFunction)) {
                        $functionStack[] = &$currentFunction;
                    }

                    $currentFunction = &$this->startLines[$lineNumber];
                }
            }

            if (isset($this->coverageData[$lineNumber])) {
                if (isset($currentClass)) {
                    $currentClass['executableLines']++;
                }

                if (isset($currentTrait)) {
                    $currentTrait['executableLines']++;
                }

                if (isset($currentMethod)) {
                    $currentMethod['executableLines']++;
                }

                if (isset($currentFunction)) {
                    $currentFunction['executableLines']++;
                }

                $this->numExecutableLines++;

                if (count($this->coverageData[$lineNumber]) > 0) {
                    if (isset($currentClass)) {
                        $currentClass['executedLines']++;
                    }

                    if (isset($currentTrait)) {
                        $currentTrait['executedLines']++;
                    }

                    if (isset($currentMethod)) {
                        $currentMethod['executedLines']++;
                    }

                    if (isset($currentFunction)) {
                        $currentFunction['executedLines']++;
                    }

                    $this->numExecutedLines++;
                }
            }

            if (isset($this->endLines[$lineNumber])) {
                // End line of a class.
                if (isset($this->endLines[$lineNumber]['className'])) {
                    unset($currentClass);

                    if ($classStack) {
                        end($classStack);
                        $key          = key($classStack);
                        $currentClass = &$classStack[$key];
                        unset($classStack[$key]);
                    }
                } // End line of a trait.
                elseif (isset($this->endLines[$lineNumber]['traitName'])) {
                    unset($currentTrait);
                } // End line of a method.
                elseif (isset($this->endLines[$lineNumber]['methodName'])) {
                    unset($currentMethod);
                } // End line of a function.
                elseif (isset($this->endLines[$lineNumber]['functionName'])) {
                    unset($currentFunction);

                    if ($functionStack) {
                        end($functionStack);
                        $key             = key($functionStack);
                        $currentFunction = &$functionStack[$key];
                        unset($functionStack[$key]);
                    }
                }
            }
        }

        foreach ($this->traits as &$trait) {
            foreach ($trait['methods'] as &$method) {
                if ($method['executableLines'] > 0) {
                    $method['coverage'] = ($method['executedLines'] /
                            $method['executableLines']) * 100;
                } else {
                    $method['coverage'] = 100;
                }

                $method['crap'] = $this->crap(
                    $method['ccn'],
                    $method['coverage']
                );

                $trait['ccn'] += $method['ccn'];
            }

            if ($trait['executableLines'] > 0) {
                $trait['coverage'] = ($trait['executedLines'] /
                        $trait['executableLines']) * 100;

                if ($trait['coverage'] == 100) {
                    $this->numTestedClasses++;
                }
            } else {
                $trait['coverage'] = 100;
            }

            $trait['crap'] = $this->crap(
                $trait['ccn'],
                $trait['coverage']
            );
        }

        foreach ($this->classes as &$class) {
            foreach ($class['methods'] as &$method) {
                if ($method['executableLines'] > 0) {
                    $method['coverage'] = ($method['executedLines'] /
                            $method['executableLines']) * 100;
                } else {
                    $method['coverage'] = 100;
                }

                $method['crap'] = $this->crap(
                    $method['ccn'],
                    $method['coverage']
                );

                $class['ccn'] += $method['ccn'];
            }

            if ($class['executableLines'] > 0) {
                $class['coverage'] = ($class['executedLines'] /
                        $class['executableLines']) * 100;

                if ($class['coverage'] == 100) {
                    $this->numTestedClasses++;
                }
            } else {
                $class['coverage'] = 100;
            }

            $class['crap'] = $this->crap(
                $class['ccn'],
                $class['coverage']
            );
        }
    }

    /**
     * @param \PHP_Token_Stream $tokens
     */
    protected function processClasses(\PHP_Token_Stream $tokens)
    {
        $classes = $tokens->getClasses();
        unset($tokens);

        $link = $this->getId() . '.html#';

        foreach ($classes as $className => $class) {
            $this->classes[$className] = [
                'className'       => $className,
                'methods'         => [],
                'startLine'       => $class['startLine'],
                'executableLines' => 0,
                'executedLines'   => 0,
                'ccn'             => 0,
                'coverage'        => 0,
                'crap'            => 0,
                'package'         => $class['package'],
                'link'            => $link . $class['startLine']
            ];

            $this->startLines[$class['startLine']] = &$this->classes[$className];
            $this->endLines[$class['endLine']]     = &$this->classes[$className];

            foreach ($class['methods'] as $methodName => $method) {
                $this->classes[$className]['methods'][$methodName] = $this->newMethod($methodName, $method, $link);

                $this->startLines[$method['startLine']] = &$this->classes[$className]['methods'][$methodName];
                $this->endLines[$method['endLine']]     = &$this->classes[$className]['methods'][$methodName];
            }
        }
    }

    /**
     * @param \PHP_Token_Stream $tokens
     */
    protected function processTraits(\PHP_Token_Stream $tokens)
    {
        $traits = $tokens->getTraits();
        unset($tokens);

        $link = $this->getId() . '.html#';

        foreach ($traits as $traitName => $trait) {
            $this->traits[$traitName] = [
                'traitName'       => $traitName,
                'methods'         => [],
                'startLine'       => $trait['startLine'],
                'executableLines' => 0,
                'executedLines'   => 0,
                'ccn'             => 0,
                'coverage'        => 0,
                'crap'            => 0,
                'package'         => $trait['package'],
                'link'            => $link . $trait['startLine']
            ];

            $this->startLines[$trait['startLine']] = &$this->traits[$traitName];
            $this->endLines[$trait['endLine']]     = &$this->traits[$traitName];

            foreach ($trait['methods'] as $methodName => $method) {
                $this->traits[$traitName]['methods'][$methodName] = $this->newMethod($methodName, $method, $link);

                $this->startLines[$method['startLine']] = &$this->traits[$traitName]['methods'][$methodName];
                $this->endLines[$method['endLine']]     = &$this->traits[$traitName]['methods'][$methodName];
            }
        }
    }

    /**
     * @param \PHP_Token_Stream $tokens
     */
    protected function processFunctions(\PHP_Token_Stream $tokens)
    {
        $functions = $tokens->getFunctions();
        unset($tokens);

        $link = $this->getId() . '.html#';

        foreach ($functions as $functionName => $function) {
            $this->functions[$functionName] = [
                'functionName'    => $functionName,
                'signature'       => $function['signature'],
                'startLine'       => $function['startLine'],
                'executableLines' => 0,
                'executedLines'   => 0,
                'ccn'             => $function['ccn'],
                'coverage'        => 0,
                'crap'            => 0,
                'link'            => $link . $function['startLine']
            ];

            $this->startLines[$function['startLine']] = &$this->functions[$functionName];
            $this->endLines[$function['endLine']]     = &$this->functions[$functionName];
        }
    }

    /**
     * Calculates the Change Risk Anti-Patterns (CRAP) index for a unit of code
     * based on its cyclomatic complexity and percentage of code coverage.
     *
     * @param int   $ccn
     * @param float $coverage
     *
     * @return string
     */
    protected function crap($ccn, $coverage)
    {
        if ($coverage == 0) {
            return (string) (pow($ccn, 2) + $ccn);
        }

        if ($coverage >= 95) {
            return (string) $ccn;
        }

        return sprintf(
            '%01.2F',
            pow($ccn, 2) * pow(1 - $coverage/100, 3) + $ccn
        );
    }

    /**
     * @param string $methodName
     * @param array  $method
     * @param string $link
     *
     * @return array
     */
    private function newMethod($methodName, array $method, $link)
    {
        return [
            'methodName'      => $methodName,
            'visibility'      => $method['visibility'],
            'signature'       => $method['signature'],
            'startLine'       => $method['startLine'],
            'endLine'         => $method['endLine'],
            'executableLines' => 0,
            'executedLines'   => 0,
            'ccn'             => $method['ccn'],
            'coverage'        => 0,
            'crap'            => 0,
            'link'            => $link . $method['startLine'],
        ];
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Node;

/**
 * Recursive iterator for node object graphs.
 */
class Iterator implements \RecursiveIterator
{
    /**
     * @var int
     */
    private $position;

    /**
     * @var AbstractNode[]
     */
    private $nodes;

    /**
     * @param Directory $node
     */
    public function __construct(Directory $node)
    {
        $this->nodes = $node->getChildNodes();
    }

    /**
     * Rewinds the Iterator to the first element.
     */
    public function rewind()
    {
        $this->position = 0;
    }

    /**
     * Checks if there is a current element after calls to rewind() or next().
     *
     * @return bool
     */
    public function valid()
    {
        return $this->position < count($this->nodes);
    }

    /**
     * Returns the key of the current element.
     *
     * @return int
     */
    public function key()
    {
        return $this->position;
    }

    /**
     * Returns the current element.
     *
     * @return \PHPUnit_Framework_Test
     */
    public function current()
    {
        return $this->valid() ? $this->nodes[$this->position] : null;
    }

    /**
     * Moves forward to next element.
     */
    public function next()
    {
        $this->position++;
    }

    /**
     * Returns the sub iterator for the current element.
     *
     * @return Iterator
     */
    public function getChildren()
    {
        return new self(
            $this->nodes[$this->position]
        );
    }

    /**
     * Checks whether the current element has children.
     *
     * @return bool
     */
    public function hasChildren()
    {
        return $this->nodes[$this->position] instanceof Directory;
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report;

use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Node\File;

/**
 * Generates a Clover XML logfile from a code coverage object.
 */
class Clover
{
    /**
     * @param CodeCoverage $coverage
     * @param string       $target
     * @param string       $name
     *
     * @return string
     */
    public function process(CodeCoverage $coverage, $target = null, $name = null)
    {
        $xmlDocument               = new \DOMDocument('1.0', 'UTF-8');
        $xmlDocument->formatOutput = true;

        $xmlCoverage = $xmlDocument->createElement('coverage');
        $xmlCoverage->setAttribute('generated', (int) $_SERVER['REQUEST_TIME']);
        $xmlDocument->appendChild($xmlCoverage);

        $xmlProject = $xmlDocument->createElement('project');
        $xmlProject->setAttribute('timestamp', (int) $_SERVER['REQUEST_TIME']);

        if (is_string($name)) {
            $xmlProject->setAttribute('name', $name);
        }

        $xmlCoverage->appendChild($xmlProject);

        $packages = [];
        $report   = $coverage->getReport();
        unset($coverage);

        foreach ($report as $item) {
            if (!$item instanceof File) {
                continue;
            }

            /* @var File $item */

            $xmlFile = $xmlDocument->createElement('file');
            $xmlFile->setAttribute('name', $item->getPath());

            $classes   = $item->getClassesAndTraits();
            $coverage  = $item->getCoverageData();
            $lines     = [];
            $namespace = 'global';

            foreach ($classes as $className => $class) {
                $classStatements        = 0;
                $coveredClassStatements = 0;
                $coveredMethods         = 0;
                $classMethods           = 0;

                foreach ($class['methods'] as $methodName => $method) {
                    if ($method['executableLines']  == 0) {
                        continue;
                    }

                    $classMethods++;
                    $classStatements        += $method['executableLines'];
                    $coveredClassStatements += $method['executedLines'];

                    if ($method['coverage'] == 100) {
                        $coveredMethods++;
                    }

                    $methodCount = 0;

                    foreach (range($method['startLine'], $method['endLine']) as $line) {
                        if (isset($coverage[$line]) && ($coverage[$line] !== null)) {
                            $methodCount = max($methodCount, count($coverage[$line]));
                        }
                    }

                    $lines[$method['startLine']] = [
                        'ccn'         => $method['ccn'],
                        'count'       => $methodCount,
                        'crap'        => $method['crap'],
                        'type'        => 'method',
                        'visibility'  => $method['visibility'],
                        'name'        => $methodName
                    ];
                }

                if (!empty($class['package']['namespace'])) {
                    $namespace = $class['package']['namespace'];
                }

                $xmlClass = $xmlDocument->createElement('class');
                $xmlClass->setAttribute('name', $className);
                $xmlClass->setAttribute('namespace', $namespace);

                if (!empty($class['package']['fullPackage'])) {
                    $xmlClass->setAttribute(
                        'fullPackage',
                        $class['package']['fullPackage']
                    );
                }

                if (!empty($class['package']['category'])) {
                    $xmlClass->setAttribute(
                        'category',
                        $class['package']['category']
                    );
                }

                if (!empty($class['package']['package'])) {
                    $xmlClass->setAttribute(
                        'package',
                        $class['package']['package']
                    );
                }

                if (!empty($class['package']['subpackage'])) {
                    $xmlClass->setAttribute(
                        'subpackage',
                        $class['package']['subpackage']
                    );
                }

                $xmlFile->appendChild($xmlClass);

                $xmlMetrics = $xmlDocument->createElement('metrics');
                $xmlMetrics->setAttribute('complexity', $class['ccn']);
                $xmlMetrics->setAttribute('methods', $classMethods);
                $xmlMetrics->setAttribute('coveredmethods', $coveredMethods);
                $xmlMetrics->setAttribute('conditionals', 0);
                $xmlMetrics->setAttribute('coveredconditionals', 0);
                $xmlMetrics->setAttribute('statements', $classStatements);
                $xmlMetrics->setAttribute('coveredstatements', $coveredClassStatements);
                $xmlMetrics->setAttribute('elements', $classMethods + $classStatements /* + conditionals */);
                $xmlMetrics->setAttribute('coveredelements', $coveredMethods + $coveredClassStatements /* + coveredconditionals */);
                $xmlClass->appendChild($xmlMetrics);
            }

            foreach ($coverage as $line => $data) {
                if ($data === null || isset($lines[$line])) {
                    continue;
                }

                $lines[$line] = [
                    'count' => count($data), 'type' => 'stmt'
                ];
            }

            ksort($lines);

            foreach ($lines as $line => $data) {
                $xmlLine = $xmlDocument->createElement('line');
                $xmlLine->setAttribute('num', $line);
                $xmlLine->setAttribute('type', $data['type']);

                if (isset($data['name'])) {
                    $xmlLine->setAttribute('name', $data['name']);
                }

                if (isset($data['visibility'])) {
                    $xmlLine->setAttribute('visibility', $data['visibility']);
                }

                if (isset($data['ccn'])) {
                    $xmlLine->setAttribute('complexity', $data['ccn']);
                }

                if (isset($data['crap'])) {
                    $xmlLine->setAttribute('crap', $data['crap']);
                }

                $xmlLine->setAttribute('count', $data['count']);
                $xmlFile->appendChild($xmlLine);
            }

            $linesOfCode = $item->getLinesOfCode();

            $xmlMetrics = $xmlDocument->createElement('metrics');
            $xmlMetrics->setAttribute('loc', $linesOfCode['loc']);
            $xmlMetrics->setAttribute('ncloc', $linesOfCode['ncloc']);
            $xmlMetrics->setAttribute('classes', $item->getNumClassesAndTraits());
            $xmlMetrics->setAttribute('methods', $item->getNumMethods());
            $xmlMetrics->setAttribute('coveredmethods', $item->getNumTestedMethods());
            $xmlMetrics->setAttribute('conditionals', 0);
            $xmlMetrics->setAttribute('coveredconditionals', 0);
            $xmlMetrics->setAttribute('statements', $item->getNumExecutableLines());
            $xmlMetrics->setAttribute('coveredstatements', $item->getNumExecutedLines());
            $xmlMetrics->setAttribute('elements', $item->getNumMethods() + $item->getNumExecutableLines() /* + conditionals */);
            $xmlMetrics->setAttribute('coveredelements', $item->getNumTestedMethods() + $item->getNumExecutedLines() /* + coveredconditionals */);
            $xmlFile->appendChild($xmlMetrics);

            if ($namespace == 'global') {
                $xmlProject->appendChild($xmlFile);
            } else {
                if (!isset($packages[$namespace])) {
                    $packages[$namespace] = $xmlDocument->createElement(
                        'package'
                    );

                    $packages[$namespace]->setAttribute('name', $namespace);
                    $xmlProject->appendChild($packages[$namespace]);
                }

                $packages[$namespace]->appendChild($xmlFile);
            }
        }

        $linesOfCode = $report->getLinesOfCode();

        $xmlMetrics = $xmlDocument->createElement('metrics');
        $xmlMetrics->setAttribute('files', count($report));
        $xmlMetrics->setAttribute('loc', $linesOfCode['loc']);
        $xmlMetrics->setAttribute('ncloc', $linesOfCode['ncloc']);
        $xmlMetrics->setAttribute('classes', $report->getNumClassesAndTraits());
        $xmlMetrics->setAttribute('methods', $report->getNumMethods());
        $xmlMetrics->setAttribute('coveredmethods', $report->getNumTestedMethods());
        $xmlMetrics->setAttribute('conditionals', 0);
        $xmlMetrics->setAttribute('coveredconditionals', 0);
        $xmlMetrics->setAttribute('statements', $report->getNumExecutableLines());
        $xmlMetrics->setAttribute('coveredstatements', $report->getNumExecutedLines());
        $xmlMetrics->setAttribute('elements', $report->getNumMethods() + $report->getNumExecutableLines() /* + conditionals */);
        $xmlMetrics->setAttribute('coveredelements', $report->getNumTestedMethods() + $report->getNumExecutedLines() /* + coveredconditionals */);
        $xmlProject->appendChild($xmlMetrics);

        $buffer = $xmlDocument->saveXML();

        if ($target !== null) {
            if (!is_dir(dirname($target))) {
                mkdir(dirname($target), 0777, true);
            }

            file_put_contents($target, $buffer);
        }

        return $buffer;
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report;

use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\CodeCoverage\InvalidArgumentException;

class Crap4j
{
    /**
     * @var int
     */
    private $threshold;

    /**
     * @param int $threshold
     */
    public function __construct($threshold = 30)
    {
        if (!is_int($threshold)) {
            throw InvalidArgumentException::create(
                1,
                'integer'
            );
        }

        $this->threshold = $threshold;
    }

    /**
     * @param CodeCoverage $coverage
     * @param string       $target
     * @param string       $name
     *
     * @return string
     */
    public function process(CodeCoverage $coverage, $target = null, $name = null)
    {
        $document               = new \DOMDocument('1.0', 'UTF-8');
        $document->formatOutput = true;

        $root = $document->createElement('crap_result');
        $document->appendChild($root);

        $project = $document->createElement('project', is_string($name) ? $name : '');
        $root->appendChild($project);
        $root->appendChild($document->createElement('timestamp', date('Y-m-d H:i:s', (int) $_SERVER['REQUEST_TIME'])));

        $stats       = $document->createElement('stats');
        $methodsNode = $document->createElement('methods');

        $report = $coverage->getReport();
        unset($coverage);

        $fullMethodCount     = 0;
        $fullCrapMethodCount = 0;
        $fullCrapLoad        = 0;
        $fullCrap            = 0;

        foreach ($report as $item) {
            $namespace = 'global';

            if (!$item instanceof File) {
                continue;
            }

            $file = $document->createElement('file');
            $file->setAttribute('name', $item->getPath());

            $classes = $item->getClassesAndTraits();

            foreach ($classes as $className => $class) {
                foreach ($class['methods'] as $methodName => $method) {
                    $crapLoad = $this->getCrapLoad($method['crap'], $method['ccn'], $method['coverage']);

                    $fullCrap     += $method['crap'];
                    $fullCrapLoad += $crapLoad;
                    $fullMethodCount++;

                    if ($method['crap'] >= $this->threshold) {
                        $fullCrapMethodCount++;
                    }

                    $methodNode = $document->createElement('method');

                    if (!empty($class['package']['namespace'])) {
                        $namespace = $class['package']['namespace'];
                    }

                    $methodNode->appendChild($document->createElement('package', $namespace));
                    $methodNode->appendChild($document->createElement('className', $className));
                    $methodNode->appendChild($document->createElement('methodName', $methodName));
                    $methodNode->appendChild($document->createElement('methodSignature', htmlspecialchars($method['signature'])));
                    $methodNode->appendChild($document->createElement('fullMethod', htmlspecialchars($method['signature'])));
                    $methodNode->appendChild($document->createElement('crap', $this->roundValue($method['crap'])));
                    $methodNode->appendChild($document->createElement('complexity', $method['ccn']));
                    $methodNode->appendChild($document->createElement('coverage', $this->roundValue($method['coverage'])));
                    $methodNode->appendChild($document->createElement('crapLoad', round($crapLoad)));

                    $methodsNode->appendChild($methodNode);
                }
            }
        }

        $stats->appendChild($document->createElement('name', 'Method Crap Stats'));
        $stats->appendChild($document->createElement('methodCount', $fullMethodCount));
        $stats->appendChild($document->createElement('crapMethodCount', $fullCrapMethodCount));
        $stats->appendChild($document->createElement('crapLoad', round($fullCrapLoad)));
        $stats->appendChild($document->createElement('totalCrap', $fullCrap));

        if ($fullMethodCount > 0) {
            $crapMethodPercent = $this->roundValue((100 * $fullCrapMethodCount) / $fullMethodCount);
        } else {
            $crapMethodPercent = 0;
        }

        $stats->appendChild($document->createElement('crapMethodPercent', $crapMethodPercent));

        $root->appendChild($stats);
        $root->appendChild($methodsNode);

        $buffer = $document->saveXML();

        if ($target !== null) {
            if (!is_dir(dirname($target))) {
                mkdir(dirname($target), 0777, true);
            }

            file_put_contents($target, $buffer);
        }

        return $buffer;
    }

    /**
     * @param float $crapValue
     * @param int   $cyclomaticComplexity
     * @param float $coveragePercent
     *
     * @return float
     */
    private function getCrapLoad($crapValue, $cyclomaticComplexity, $coveragePercent)
    {
        $crapLoad = 0;

        if ($crapValue >= $this->threshold) {
            $crapLoad += $cyclomaticComplexity * (1.0 - $coveragePercent / 100);
            $crapLoad += $cyclomaticComplexity / $this->threshold;
        }

        return $crapLoad;
    }

    /**
     * @param float $value
     *
     * @return float
     */
    private function roundValue($value)
    {
        return round($value, 2);
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report\Html;

use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
use SebastianBergmann\CodeCoverage\RuntimeException;

/**
 * Generates an HTML report from a code coverage object.
 */
class Facade
{
    /**
     * @var string
     */
    private $templatePath;

    /**
     * @var string
     */
    private $generator;

    /**
     * @var int
     */
    private $lowUpperBound;

    /**
     * @var int
     */
    private $highLowerBound;

    /**
     * Constructor.
     *
     * @param int    $lowUpperBound
     * @param int    $highLowerBound
     * @param string $generator
     */
    public function __construct($lowUpperBound = 50, $highLowerBound = 90, $generator = '')
    {
        $this->generator      = $generator;
        $this->highLowerBound = $highLowerBound;
        $this->lowUpperBound  = $lowUpperBound;
        $this->templatePath   = __DIR__ . '/Renderer/Template/';
    }

    /**
     * @param CodeCoverage $coverage
     * @param string       $target
     */
    public function process(CodeCoverage $coverage, $target)
    {
        $target = $this->getDirectory($target);
        $report = $coverage->getReport();
        unset($coverage);

        if (!isset($_SERVER['REQUEST_TIME'])) {
            $_SERVER['REQUEST_TIME'] = time();
        }

        $date = date('D M j G:i:s T Y', $_SERVER['REQUEST_TIME']);

        $dashboard = new Dashboard(
            $this->templatePath,
            $this->generator,
            $date,
            $this->lowUpperBound,
            $this->highLowerBound
        );

        $directory = new Directory(
            $this->templatePath,
            $this->generator,
            $date,
            $this->lowUpperBound,
            $this->highLowerBound
        );

        $file = new File(
            $this->templatePath,
            $this->generator,
            $date,
            $this->lowUpperBound,
            $this->highLowerBound
        );

        $directory->render($report, $target . 'index.html');
        $dashboard->render($report, $target . 'dashboard.html');

        foreach ($report as $node) {
            $id = $node->getId();

            if ($node instanceof DirectoryNode) {
                if (!file_exists($target . $id)) {
                    mkdir($target . $id, 0777, true);
                }

                $directory->render($node, $target . $id . '/index.html');
                $dashboard->render($node, $target . $id . '/dashboard.html');
            } else {
                $dir = dirname($target . $id);

                if (!file_exists($dir)) {
                    mkdir($dir, 0777, true);
                }

                $file->render($node, $target . $id . '.html');
            }
        }

        $this->copyFiles($target);
    }

    /**
     * @param string $target
     */
    private function copyFiles($target)
    {
        $dir = $this->getDirectory($target . 'css');
        copy($this->templatePath . 'css/bootstrap.min.css', $dir . 'bootstrap.min.css');
        copy($this->templatePath . 'css/nv.d3.min.css', $dir . 'nv.d3.min.css');
        copy($this->templatePath . 'css/style.css', $dir . 'style.css');

        $dir = $this->getDirectory($target . 'fonts');
        copy($this->templatePath . 'fonts/glyphicons-halflings-regular.eot', $dir . 'glyphicons-halflings-regular.eot');
        copy($this->templatePath . 'fonts/glyphicons-halflings-regular.svg', $dir . 'glyphicons-halflings-regular.svg');
        copy($this->templatePath . 'fonts/glyphicons-halflings-regular.ttf', $dir . 'glyphicons-halflings-regular.ttf');
        copy($this->templatePath . 'fonts/glyphicons-halflings-regular.woff', $dir . 'glyphicons-halflings-regular.woff');
        copy($this->templatePath . 'fonts/glyphicons-halflings-regular.woff2', $dir . 'glyphicons-halflings-regular.woff2');

        $dir = $this->getDirectory($target . 'js');
        copy($this->templatePath . 'js/bootstrap.min.js', $dir . 'bootstrap.min.js');
        copy($this->templatePath . 'js/d3.min.js', $dir . 'd3.min.js');
        copy($this->templatePath . 'js/holder.min.js', $dir . 'holder.min.js');
        copy($this->templatePath . 'js/html5shiv.min.js', $dir . 'html5shiv.min.js');
        copy($this->templatePath . 'js/jquery.min.js', $dir . 'jquery.min.js');
        copy($this->templatePath . 'js/nv.d3.min.js', $dir . 'nv.d3.min.js');
        copy($this->templatePath . 'js/respond.min.js', $dir . 'respond.min.js');
    }

    /**
     * @param string $directory
     *
     * @return string
     *
     * @throws RuntimeException
     */
    private function getDirectory($directory)
    {
        if (substr($directory, -1, 1) != DIRECTORY_SEPARATOR) {
            $directory .= DIRECTORY_SEPARATOR;
        }

        if (is_dir($directory)) {
            return $directory;
        }

        if (@mkdir($directory, 0777, true)) {
            return $directory;
        }

        throw new RuntimeException(
            sprintf(
                'Directory "%s" does not exist.',
                $directory
            )
        );
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report\Html;

use SebastianBergmann\CodeCoverage\Node\AbstractNode;
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;

/**
 * Renders the dashboard for a directory node.
 */
class Dashboard extends Renderer
{
    /**
     * @param DirectoryNode $node
     * @param string        $file
     */
    public function render(DirectoryNode $node, $file)
    {
        $classes  = $node->getClassesAndTraits();
        $template = new \Text_Template(
            $this->templatePath . 'dashboard.html',
            '{{',
            '}}'
        );

        $this->setCommonTemplateVariables($template, $node);

        $baseLink             = $node->getId() . '/';
        $complexity           = $this->complexity($classes, $baseLink);
        $coverageDistribution = $this->coverageDistribution($classes);
        $insufficientCoverage = $this->insufficientCoverage($classes, $baseLink);
        $projectRisks         = $this->projectRisks($classes, $baseLink);

        $template->setVar(
            [
                'insufficient_coverage_classes' => $insufficientCoverage['class'],
                'insufficient_coverage_methods' => $insufficientCoverage['method'],
                'project_risks_classes'         => $projectRisks['class'],
                'project_risks_methods'         => $projectRisks['method'],
                'complexity_class'              => $complexity['class'],
                'complexity_method'             => $complexity['method'],
                'class_coverage_distribution'   => $coverageDistribution['class'],
                'method_coverage_distribution'  => $coverageDistribution['method']
            ]
        );

        $template->renderTo($file);
    }

    /**
     * Returns the data for the Class/Method Complexity charts.
     *
     * @param array  $classes
     * @param string $baseLink
     *
     * @return array
     */
    protected function complexity(array $classes, $baseLink)
    {
        $result = ['class' => [], 'method' => []];

        foreach ($classes as $className => $class) {
            foreach ($class['methods'] as $methodName => $method) {
                if ($className != '*') {
                    $methodName = $className . '::' . $methodName;
                }

                $result['method'][] = [
                    $method['coverage'],
                    $method['ccn'],
                    sprintf(
                        '<a href="%s">%s</a>',
                        str_replace($baseLink, '', $method['link']),
                        $methodName
                    )
                ];
            }

            $result['class'][] = [
                $class['coverage'],
                $class['ccn'],
                sprintf(
                    '<a href="%s">%s</a>',
                    str_replace($baseLink, '', $class['link']),
                    $className
                )
            ];
        }

        return [
            'class'  => json_encode($result['class']),
            'method' => json_encode($result['method'])
        ];
    }

    /**
     * Returns the data for the Class / Method Coverage Distribution chart.
     *
     * @param array $classes
     *
     * @return array
     */
    protected function coverageDistribution(array $classes)
    {
        $result = [
            'class' => [
                '0%'      => 0,
                '0-10%'   => 0,
                '10-20%'  => 0,
                '20-30%'  => 0,
                '30-40%'  => 0,
                '40-50%'  => 0,
                '50-60%'  => 0,
                '60-70%'  => 0,
                '70-80%'  => 0,
                '80-90%'  => 0,
                '90-100%' => 0,
                '100%'    => 0
            ],
            'method' => [
                '0%'      => 0,
                '0-10%'   => 0,
                '10-20%'  => 0,
                '20-30%'  => 0,
                '30-40%'  => 0,
                '40-50%'  => 0,
                '50-60%'  => 0,
                '60-70%'  => 0,
                '70-80%'  => 0,
                '80-90%'  => 0,
                '90-100%' => 0,
                '100%'    => 0
            ]
        ];

        foreach ($classes as $class) {
            foreach ($class['methods'] as $methodName => $method) {
                if ($method['coverage'] == 0) {
                    $result['method']['0%']++;
                } elseif ($method['coverage'] == 100) {
                    $result['method']['100%']++;
                } else {
                    $key = floor($method['coverage'] / 10) * 10;
                    $key = $key . '-' . ($key + 10) . '%';
                    $result['method'][$key]++;
                }
            }

            if ($class['coverage'] == 0) {
                $result['class']['0%']++;
            } elseif ($class['coverage'] == 100) {
                $result['class']['100%']++;
            } else {
                $key = floor($class['coverage'] / 10) * 10;
                $key = $key . '-' . ($key + 10) . '%';
                $result['class'][$key]++;
            }
        }

        return [
            'class'  => json_encode(array_values($result['class'])),
            'method' => json_encode(array_values($result['method']))
        ];
    }

    /**
     * Returns the classes / methods with insufficient coverage.
     *
     * @param array  $classes
     * @param string $baseLink
     *
     * @return array
     */
    protected function insufficientCoverage(array $classes, $baseLink)
    {
        $leastTestedClasses = [];
        $leastTestedMethods = [];
        $result             = ['class' => '', 'method' => ''];

        foreach ($classes as $className => $class) {
            foreach ($class['methods'] as $methodName => $method) {
                if ($method['coverage'] < $this->highLowerBound) {
                    if ($className != '*') {
                        $key = $className . '::' . $methodName;
                    } else {
                        $key = $methodName;
                    }

                    $leastTestedMethods[$key] = $method['coverage'];
                }
            }

            if ($class['coverage'] < $this->highLowerBound) {
                $leastTestedClasses[$className] = $class['coverage'];
            }
        }

        asort($leastTestedClasses);
        asort($leastTestedMethods);

        foreach ($leastTestedClasses as $className => $coverage) {
            $result['class'] .= sprintf(
                '       <tr><td><a href="%s">%s</a></td><td class="text-right">%d%%</td></tr>' . "\n",
                str_replace($baseLink, '', $classes[$className]['link']),
                $className,
                $coverage
            );
        }

        foreach ($leastTestedMethods as $methodName => $coverage) {
            list($class, $method) = explode('::', $methodName);

            $result['method'] .= sprintf(
                '       <tr><td><a href="%s"><abbr title="%s">%s</abbr></a></td><td class="text-right">%d%%</td></tr>' . "\n",
                str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']),
                $methodName,
                $method,
                $coverage
            );
        }

        return $result;
    }

    /**
     * Returns the project risks according to the CRAP index.
     *
     * @param array  $classes
     * @param string $baseLink
     *
     * @return array
     */
    protected function projectRisks(array $classes, $baseLink)
    {
        $classRisks  = [];
        $methodRisks = [];
        $result      = ['class' => '', 'method' => ''];

        foreach ($classes as $className => $class) {
            foreach ($class['methods'] as $methodName => $method) {
                if ($method['coverage'] < $this->highLowerBound &&
                    $method['ccn'] > 1) {
                    if ($className != '*') {
                        $key = $className . '::' . $methodName;
                    } else {
                        $key = $methodName;
                    }

                    $methodRisks[$key] = $method['crap'];
                }
            }

            if ($class['coverage'] < $this->highLowerBound &&
                $class['ccn'] > count($class['methods'])) {
                $classRisks[$className] = $class['crap'];
            }
        }

        arsort($classRisks);
        arsort($methodRisks);

        foreach ($classRisks as $className => $crap) {
            $result['class'] .= sprintf(
                '       <tr><td><a href="%s">%s</a></td><td class="text-right">%d</td></tr>' . "\n",
                str_replace($baseLink, '', $classes[$className]['link']),
                $className,
                $crap
            );
        }

        foreach ($methodRisks as $methodName => $crap) {
            list($class, $method) = explode('::', $methodName);

            $result['method'] .= sprintf(
                '       <tr><td><a href="%s"><abbr title="%s">%s</abbr></a></td><td class="text-right">%d</td></tr>' . "\n",
                str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']),
                $methodName,
                $method,
                $crap
            );
        }

        return $result;
    }

    protected function getActiveBreadcrumb(AbstractNode $node)
    {
        return sprintf(
            '        <li><a href="index.html">%s</a></li>' . "\n" .
            '        <li class="active">(Dashboard)</li>' . "\n",
            $node->getName()
        );
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report\Html;

use SebastianBergmann\CodeCoverage\Node\AbstractNode as Node;
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;

/**
 * Renders a directory node.
 */
class Directory extends Renderer
{
    /**
     * @param DirectoryNode $node
     * @param string        $file
     */
    public function render(DirectoryNode $node, $file)
    {
        $template = new \Text_Template($this->templatePath . 'directory.html', '{{', '}}');

        $this->setCommonTemplateVariables($template, $node);

        $items = $this->renderItem($node, true);

        foreach ($node->getDirectories() as $item) {
            $items .= $this->renderItem($item);
        }

        foreach ($node->getFiles() as $item) {
            $items .= $this->renderItem($item);
        }

        $template->setVar(
            [
                'id'    => $node->getId(),
                'items' => $items
            ]
        );

        $template->renderTo($file);
    }

    /**
     * @param Node $node
     * @param bool $total
     *
     * @return string
     */
    protected function renderItem(Node $node, $total = false)
    {
        $data = [
            'numClasses'                   => $node->getNumClassesAndTraits(),
            'numTestedClasses'             => $node->getNumTestedClassesAndTraits(),
            'numMethods'                   => $node->getNumMethods(),
            'numTestedMethods'             => $node->getNumTestedMethods(),
            'linesExecutedPercent'         => $node->getLineExecutedPercent(false),
            'linesExecutedPercentAsString' => $node->getLineExecutedPercent(),
            'numExecutedLines'             => $node->getNumExecutedLines(),
            'numExecutableLines'           => $node->getNumExecutableLines(),
            'testedMethodsPercent'         => $node->getTestedMethodsPercent(false),
            'testedMethodsPercentAsString' => $node->getTestedMethodsPercent(),
            'testedClassesPercent'         => $node->getTestedClassesAndTraitsPercent(false),
            'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent()
        ];

        if ($total) {
            $data['name'] = 'Total';
        } else {
            if ($node instanceof DirectoryNode) {
                $data['name'] = sprintf(
                    '<a href="%s/index.html">%s</a>',
                    $node->getName(),
                    $node->getName()
                );

                $data['icon'] = '<span class="glyphicon glyphicon-folder-open"></span> ';
            } else {
                $data['name'] = sprintf(
                    '<a href="%s.html">%s</a>',
                    $node->getName(),
                    $node->getName()
                );

                $data['icon'] = '<span class="glyphicon glyphicon-file"></span> ';
            }
        }

        return $this->renderItemTemplate(
            new \Text_Template($this->templatePath . 'directory_item.html', '{{', '}}'),
            $data
        );
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report\Html;

use SebastianBergmann\CodeCoverage\Node\File as FileNode;
use SebastianBergmann\CodeCoverage\Util;

/**
 * Renders a file node.
 */
class File extends Renderer
{
    /**
     * @var int
     */
    private $htmlspecialcharsFlags;

    /**
     * Constructor.
     *
     * @param string $templatePath
     * @param string $generator
     * @param string $date
     * @param int    $lowUpperBound
     * @param int    $highLowerBound
     */
    public function __construct($templatePath, $generator, $date, $lowUpperBound, $highLowerBound)
    {
        parent::__construct(
            $templatePath,
            $generator,
            $date,
            $lowUpperBound,
            $highLowerBound
        );

        $this->htmlspecialcharsFlags = ENT_COMPAT;

        $this->htmlspecialcharsFlags = $this->htmlspecialcharsFlags | ENT_HTML401 | ENT_SUBSTITUTE;
    }

    /**
     * @param FileNode $node
     * @param string   $file
     */
    public function render(FileNode $node, $file)
    {
        $template = new \Text_Template($this->templatePath . 'file.html', '{{', '}}');

        $template->setVar(
            [
                'items' => $this->renderItems($node),
                'lines' => $this->renderSource($node)
            ]
        );

        $this->setCommonTemplateVariables($template, $node);

        $template->renderTo($file);
    }

    /**
     * @param FileNode $node
     *
     * @return string
     */
    protected function renderItems(FileNode $node)
    {
        $template = new \Text_Template($this->templatePath . 'file_item.html', '{{', '}}');

        $methodItemTemplate = new \Text_Template(
            $this->templatePath . 'method_item.html',
            '{{',
            '}}'
        );

        $items = $this->renderItemTemplate(
            $template,
            [
                'name'                         => 'Total',
                'numClasses'                   => $node->getNumClassesAndTraits(),
                'numTestedClasses'             => $node->getNumTestedClassesAndTraits(),
                'numMethods'                   => $node->getNumMethods(),
                'numTestedMethods'             => $node->getNumTestedMethods(),
                'linesExecutedPercent'         => $node->getLineExecutedPercent(false),
                'linesExecutedPercentAsString' => $node->getLineExecutedPercent(),
                'numExecutedLines'             => $node->getNumExecutedLines(),
                'numExecutableLines'           => $node->getNumExecutableLines(),
                'testedMethodsPercent'         => $node->getTestedMethodsPercent(false),
                'testedMethodsPercentAsString' => $node->getTestedMethodsPercent(),
                'testedClassesPercent'         => $node->getTestedClassesAndTraitsPercent(false),
                'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent(),
                'crap'                         => '<abbr title="Change Risk Anti-Patterns (CRAP) Index">CRAP</abbr>'
            ]
        );

        $items .= $this->renderFunctionItems(
            $node->getFunctions(),
            $methodItemTemplate
        );

        $items .= $this->renderTraitOrClassItems(
            $node->getTraits(),
            $template,
            $methodItemTemplate
        );

        $items .= $this->renderTraitOrClassItems(
            $node->getClasses(),
            $template,
            $methodItemTemplate
        );

        return $items;
    }

    /**
     * @param array          $items
     * @param \Text_Template $template
     * @param \Text_Template $methodItemTemplate
     *
     * @return string
     */
    protected function renderTraitOrClassItems(array $items, \Text_Template $template, \Text_Template $methodItemTemplate)
    {
        if (empty($items)) {
            return '';
        }

        $buffer = '';

        foreach ($items as $name => $item) {
            $numMethods       = count($item['methods']);
            $numTestedMethods = 0;

            foreach ($item['methods'] as $method) {
                if ($method['executedLines'] == $method['executableLines']) {
                    $numTestedMethods++;
                }
            }

            if ($item['executableLines'] > 0) {
                $numClasses                   = 1;
                $numTestedClasses             = $numTestedMethods == $numMethods ? 1 : 0;
                $linesExecutedPercentAsString = Util::percent(
                    $item['executedLines'],
                    $item['executableLines'],
                    true
                );
            } else {
                $numClasses                   = 'n/a';
                $numTestedClasses             = 'n/a';
                $linesExecutedPercentAsString = 'n/a';
            }

            $buffer .= $this->renderItemTemplate(
                $template,
                [
                    'name'                         => $name,
                    'numClasses'                   => $numClasses,
                    'numTestedClasses'             => $numTestedClasses,
                    'numMethods'                   => $numMethods,
                    'numTestedMethods'             => $numTestedMethods,
                    'linesExecutedPercent'         => Util::percent(
                        $item['executedLines'],
                        $item['executableLines'],
                        false
                    ),
                    'linesExecutedPercentAsString' => $linesExecutedPercentAsString,
                    'numExecutedLines'             => $item['executedLines'],
                    'numExecutableLines'           => $item['executableLines'],
                    'testedMethodsPercent'         => Util::percent(
                        $numTestedMethods,
                        $numMethods,
                        false
                    ),
                    'testedMethodsPercentAsString' => Util::percent(
                        $numTestedMethods,
                        $numMethods,
                        true
                    ),
                    'testedClassesPercent'         => Util::percent(
                        $numTestedMethods == $numMethods ? 1 : 0,
                        1,
                        false
                    ),
                    'testedClassesPercentAsString' => Util::percent(
                        $numTestedMethods == $numMethods ? 1 : 0,
                        1,
                        true
                    ),
                    'crap'                         => $item['crap']
                ]
            );

            foreach ($item['methods'] as $method) {
                $buffer .= $this->renderFunctionOrMethodItem(
                    $methodItemTemplate,
                    $method,
                    '&nbsp;'
                );
            }
        }

        return $buffer;
    }

    /**
     * @param array          $functions
     * @param \Text_Template $template
     *
     * @return string
     */
    protected function renderFunctionItems(array $functions, \Text_Template $template)
    {
        if (empty($functions)) {
            return '';
        }

        $buffer = '';

        foreach ($functions as $function) {
            $buffer .= $this->renderFunctionOrMethodItem(
                $template,
                $function
            );
        }

        return $buffer;
    }

    /**
     * @param \Text_Template $template
     *
     * @return string
     */
    protected function renderFunctionOrMethodItem(\Text_Template $template, array $item, $indent = '')
    {
        $numTestedItems = $item['executedLines'] == $item['executableLines'] ? 1 : 0;

        return $this->renderItemTemplate(
            $template,
            [
                'name'                         => sprintf(
                    '%s<a href="#%d"><abbr title="%s">%s</abbr></a>',
                    $indent,
                    $item['startLine'],
                    htmlspecialchars($item['signature']),
                    isset($item['functionName']) ? $item['functionName'] : $item['methodName']
                ),
                'numMethods'                   => 1,
                'numTestedMethods'             => $numTestedItems,
                'linesExecutedPercent'         => Util::percent(
                    $item['executedLines'],
                    $item['executableLines'],
                    false
                ),
                'linesExecutedPercentAsString' => Util::percent(
                    $item['executedLines'],
                    $item['executableLines'],
                    true
                ),
                'numExecutedLines'             => $item['executedLines'],
                'numExecutableLines'           => $item['executableLines'],
                'testedMethodsPercent'         => Util::percent(
                    $numTestedItems,
                    1,
                    false
                ),
                'testedMethodsPercentAsString' => Util::percent(
                    $numTestedItems,
                    1,
                    true
                ),
                'crap'                         => $item['crap']
            ]
        );
    }

    /**
     * @param FileNode $node
     *
     * @return string
     */
    protected function renderSource(FileNode $node)
    {
        $coverageData = $node->getCoverageData();
        $testData     = $node->getTestData();
        $codeLines    = $this->loadFile($node->getPath());
        $lines        = '';
        $i            = 1;

        foreach ($codeLines as $line) {
            $trClass        = '';
            $popoverContent = '';
            $popoverTitle   = '';

            if (array_key_exists($i, $coverageData)) {
                $numTests = count($coverageData[$i]);

                if ($coverageData[$i] === null) {
                    $trClass = ' class="warning"';
                } elseif ($numTests == 0) {
                    $trClass = ' class="danger"';
                } else {
                    $lineCss        = 'covered-by-large-tests';
                    $popoverContent = '<ul>';

                    if ($numTests > 1) {
                        $popoverTitle = $numTests . ' tests cover line ' . $i;
                    } else {
                        $popoverTitle = '1 test covers line ' . $i;
                    }

                    foreach ($coverageData[$i] as $test) {
                        if ($lineCss == 'covered-by-large-tests' && $testData[$test]['size'] == 'medium') {
                            $lineCss = 'covered-by-medium-tests';
                        } elseif ($testData[$test]['size'] == 'small') {
                            $lineCss = 'covered-by-small-tests';
                        }

                        switch ($testData[$test]['status']) {
                            case 0:
                                switch ($testData[$test]['size']) {
                                    case 'small':
                                        $testCSS = ' class="covered-by-small-tests"';
                                        break;

                                    case 'medium':
                                        $testCSS = ' class="covered-by-medium-tests"';
                                        break;

                                    default:
                                        $testCSS = ' class="covered-by-large-tests"';
                                        break;
                                }
                                break;

                            case 1:
                            case 2:
                                $testCSS = ' class="warning"';
                                break;

                            case 3:
                                $testCSS = ' class="danger"';
                                break;

                            case 4:
                                $testCSS = ' class="danger"';
                                break;

                            default:
                                $testCSS = '';
                        }

                        $popoverContent .= sprintf(
                            '<li%s>%s</li>',
                            $testCSS,
                            htmlspecialchars($test)
                        );
                    }

                    $popoverContent .= '</ul>';
                    $trClass         = ' class="' . $lineCss . ' popin"';
                }
            }

            if (!empty($popoverTitle)) {
                $popover = sprintf(
                    ' data-title="%s" data-content="%s" data-placement="bottom" data-html="true"',
                    $popoverTitle,
                    htmlspecialchars($popoverContent)
                );
            } else {
                $popover = '';
            }

            $lines .= sprintf(
                '     <tr%s%s><td><div align="right"><a name="%d"></a><a href="#%d">%d</a></div></td><td class="codeLine">%s</td></tr>' . "\n",
                $trClass,
                $popover,
                $i,
                $i,
                $i,
                $line
            );

            $i++;
        }

        return $lines;
    }

    /**
     * @param string $file
     *
     * @return array
     */
    protected function loadFile($file)
    {
        $buffer              = file_get_contents($file);
        $tokens              = token_get_all($buffer);
        $result              = [''];
        $i                   = 0;
        $stringFlag          = false;
        $fileEndsWithNewLine = substr($buffer, -1) == "\n";

        unset($buffer);

        foreach ($tokens as $j => $token) {
            if (is_string($token)) {
                if ($token === '"' && $tokens[$j - 1] !== '\\') {
                    $result[$i] .= sprintf(
                        '<span class="string">%s</span>',
                        htmlspecialchars($token)
                    );

                    $stringFlag = !$stringFlag;
                } else {
                    $result[$i] .= sprintf(
                        '<span class="keyword">%s</span>',
                        htmlspecialchars($token)
                    );
                }

                continue;
            }

            list($token, $value) = $token;

            $value = str_replace(
                ["\t", ' '],
                ['&nbsp;&nbsp;&nbsp;&nbsp;', '&nbsp;'],
                htmlspecialchars($value, $this->htmlspecialcharsFlags)
            );

            if ($value === "\n") {
                $result[++$i] = '';
            } else {
                $lines = explode("\n", $value);

                foreach ($lines as $jj => $line) {
                    $line = trim($line);

                    if ($line !== '') {
                        if ($stringFlag) {
                            $colour = 'string';
                        } else {
                            switch ($token) {
                                case T_INLINE_HTML:
                                    $colour = 'html';
                                    break;

                                case T_COMMENT:
                                case T_DOC_COMMENT:
                                    $colour = 'comment';
                                    break;

                                case T_ABSTRACT:
                                case T_ARRAY:
                                case T_AS:
                                case T_BREAK:
                                case T_CALLABLE:
                                case T_CASE:
                                case T_CATCH:
                                case T_CLASS:
                                case T_CLONE:
                                case T_CONTINUE:
                                case T_DEFAULT:
                                case T_ECHO:
                                case T_ELSE:
                                case T_ELSEIF:
                                case T_EMPTY:
                                case T_ENDDECLARE:
                                case T_ENDFOR:
                                case T_ENDFOREACH:
                                case T_ENDIF:
                                case T_ENDSWITCH:
                                case T_ENDWHILE:
                                case T_EXIT:
                                case T_EXTENDS:
                                case T_FINAL:
                                case T_FINALLY:
                                case T_FOREACH:
                                case T_FUNCTION:
                                case T_GLOBAL:
                                case T_IF:
                                case T_IMPLEMENTS:
                                case T_INCLUDE:
                                case T_INCLUDE_ONCE:
                                case T_INSTANCEOF:
                                case T_INSTEADOF:
                                case T_INTERFACE:
                                case T_ISSET:
                                case T_LOGICAL_AND:
                                case T_LOGICAL_OR:
                                case T_LOGICAL_XOR:
                                case T_NAMESPACE:
                                case T_NEW:
                                case T_PRIVATE:
                                case T_PROTECTED:
                                case T_PUBLIC:
                                case T_REQUIRE:
                                case T_REQUIRE_ONCE:
                                case T_RETURN:
                                case T_STATIC:
                                case T_THROW:
                                case T_TRAIT:
                                case T_TRY:
                                case T_UNSET:
                                case T_USE:
                                case T_VAR:
                                case T_WHILE:
                                case T_YIELD:
                                    $colour = 'keyword';
                                    break;

                                default:
                                    $colour = 'default';
                            }
                        }

                        $result[$i] .= sprintf(
                            '<span class="%s">%s</span>',
                            $colour,
                            $line
                        );
                    }

                    if (isset($lines[$jj + 1])) {
                        $result[++$i] = '';
                    }
                }
            }
        }

        if ($fileEndsWithNewLine) {
            unset($result[count($result)-1]);
        }

        return $result;
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report\Html;

use SebastianBergmann\CodeCoverage\Node\AbstractNode;
use SebastianBergmann\CodeCoverage\Node\File as FileNode;
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
use SebastianBergmann\Environment\Runtime;
use SebastianBergmann\Version;

/**
 * Base class for node renderers.
 */
abstract class Renderer
{
    /**
     * @var string
     */
    protected $templatePath;

    /**
     * @var string
     */
    protected $generator;

    /**
     * @var string
     */
    protected $date;

    /**
     * @var int
     */
    protected $lowUpperBound;

    /**
     * @var int
     */
    protected $highLowerBound;

    /**
     * @var string
     */
    protected $version;

    /**
     * Constructor.
     *
     * @param string $templatePath
     * @param string $generator
     * @param string $date
     * @param int    $lowUpperBound
     * @param int    $highLowerBound
     */
    public function __construct($templatePath, $generator, $date, $lowUpperBound, $highLowerBound)
    {
        $version = new Version('4.0.8', dirname(dirname(dirname(dirname(__DIR__)))));

        $this->templatePath   = $templatePath;
        $this->generator      = $generator;
        $this->date           = $date;
        $this->lowUpperBound  = $lowUpperBound;
        $this->highLowerBound = $highLowerBound;
        $this->version        = $version->getVersion();
    }

    /**
     * @param \Text_Template $template
     * @param array          $data
     *
     * @return string
     */
    protected function renderItemTemplate(\Text_Template $template, array $data)
    {
        $numSeparator  = '&nbsp;/&nbsp;';

        if (isset($data['numClasses']) && $data['numClasses'] > 0) {
            $classesLevel = $this->getColorLevel($data['testedClassesPercent']);

            $classesNumber = $data['numTestedClasses'] . $numSeparator .
                $data['numClasses'];

            $classesBar = $this->getCoverageBar(
                $data['testedClassesPercent']
            );
        } else {
            $classesLevel                         = '';
            $classesNumber                        = '0' . $numSeparator . '0';
            $classesBar                           = '';
            $data['testedClassesPercentAsString'] = 'n/a';
        }

        if ($data['numMethods'] > 0) {
            $methodsLevel = $this->getColorLevel($data['testedMethodsPercent']);

            $methodsNumber = $data['numTestedMethods'] . $numSeparator .
                $data['numMethods'];

            $methodsBar = $this->getCoverageBar(
                $data['testedMethodsPercent']
            );
        } else {
            $methodsLevel                         = '';
            $methodsNumber                        = '0' . $numSeparator . '0';
            $methodsBar                           = '';
            $data['testedMethodsPercentAsString'] = 'n/a';
        }

        if ($data['numExecutableLines'] > 0) {
            $linesLevel = $this->getColorLevel($data['linesExecutedPercent']);

            $linesNumber = $data['numExecutedLines'] . $numSeparator .
                $data['numExecutableLines'];

            $linesBar = $this->getCoverageBar(
                $data['linesExecutedPercent']
            );
        } else {
            $linesLevel                           = '';
            $linesNumber                          = '0' . $numSeparator . '0';
            $linesBar                             = '';
            $data['linesExecutedPercentAsString'] = 'n/a';
        }

        $template->setVar(
            [
                'icon'                   => isset($data['icon']) ? $data['icon'] : '',
                'crap'                   => isset($data['crap']) ? $data['crap'] : '',
                'name'                   => $data['name'],
                'lines_bar'              => $linesBar,
                'lines_executed_percent' => $data['linesExecutedPercentAsString'],
                'lines_level'            => $linesLevel,
                'lines_number'           => $linesNumber,
                'methods_bar'            => $methodsBar,
                'methods_tested_percent' => $data['testedMethodsPercentAsString'],
                'methods_level'          => $methodsLevel,
                'methods_number'         => $methodsNumber,
                'classes_bar'            => $classesBar,
                'classes_tested_percent' => isset($data['testedClassesPercentAsString']) ? $data['testedClassesPercentAsString'] : '',
                'classes_level'          => $classesLevel,
                'classes_number'         => $classesNumber
            ]
        );

        return $template->render();
    }

    /**
     * @param \Text_Template $template
     * @param AbstractNode   $node
     */
    protected function setCommonTemplateVariables(\Text_Template $template, AbstractNode $node)
    {
        $template->setVar(
            [
                'id'               => $node->getId(),
                'full_path'        => $node->getPath(),
                'path_to_root'     => $this->getPathToRoot($node),
                'breadcrumbs'      => $this->getBreadcrumbs($node),
                'date'             => $this->date,
                'version'          => $this->version,
                'runtime'          => $this->getRuntimeString(),
                'generator'        => $this->generator,
                'low_upper_bound'  => $this->lowUpperBound,
                'high_lower_bound' => $this->highLowerBound
            ]
        );
    }

    protected function getBreadcrumbs(AbstractNode $node)
    {
        $breadcrumbs = '';
        $path        = $node->getPathAsArray();
        $pathToRoot  = [];
        $max         = count($path);

        if ($node instanceof FileNode) {
            $max--;
        }

        for ($i = 0; $i < $max; $i++) {
            $pathToRoot[] = str_repeat('../', $i);
        }

        foreach ($path as $step) {
            if ($step !== $node) {
                $breadcrumbs .= $this->getInactiveBreadcrumb(
                    $step,
                    array_pop($pathToRoot)
                );
            } else {
                $breadcrumbs .= $this->getActiveBreadcrumb($step);
            }
        }

        return $breadcrumbs;
    }

    protected function getActiveBreadcrumb(AbstractNode $node)
    {
        $buffer = sprintf(
            '        <li class="active">%s</li>' . "\n",
            $node->getName()
        );

        if ($node instanceof DirectoryNode) {
            $buffer .= '        <li>(<a href="dashboard.html">Dashboard</a>)</li>' . "\n";
        }

        return $buffer;
    }

    protected function getInactiveBreadcrumb(AbstractNode $node, $pathToRoot)
    {
        return sprintf(
            '        <li><a href="%sindex.html">%s</a></li>' . "\n",
            $pathToRoot,
            $node->getName()
        );
    }

    protected function getPathToRoot(AbstractNode $node)
    {
        $id    = $node->getId();
        $depth = substr_count($id, '/');

        if ($id != 'index' &&
            $node instanceof DirectoryNode) {
            $depth++;
        }

        return str_repeat('../', $depth);
    }

    protected function getCoverageBar($percent)
    {
        $level = $this->getColorLevel($percent);

        $template = new \Text_Template(
            $this->templatePath . 'coverage_bar.html',
            '{{',
            '}}'
        );

        $template->setVar(['level' => $level, 'percent' => sprintf('%.2F', $percent)]);

        return $template->render();
    }

    /**
     * @param int $percent
     *
     * @return string
     */
    protected function getColorLevel($percent)
    {
        if ($percent <= $this->lowUpperBound) {
            return 'danger';
        } elseif ($percent > $this->lowUpperBound &&
            $percent <  $this->highLowerBound) {
            return 'warning';
        } else {
            return 'success';
        }
    }

    /**
     * @return string
     */
    private function getRuntimeString()
    {
        $runtime = new Runtime;

        $buffer = sprintf(
            '<a href="%s" target="_top">%s %s</a>',
            $runtime->getVendorUrl(),
            $runtime->getName(),
            $runtime->getVersion()
        );

        if ($runtime->hasXdebug() && !$runtime->hasPHPDBGCodeCoverage()) {
            $buffer .= sprintf(
                ' with <a href="https://xdebug.org/">Xdebug %s</a>',
                phpversion('xdebug')
            );
        }

        return $buffer;
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report;

use SebastianBergmann\CodeCoverage\CodeCoverage;

/**
 * Uses var_export() to write a SebastianBergmann\CodeCoverage\CodeCoverage object to a file.
 */
class PHP
{
    /**
     * @param CodeCoverage $coverage
     * @param string       $target
     *
     * @return string
     */
    public function process(CodeCoverage $coverage, $target = null)
    {
        $filter = $coverage->filter();

        $output = sprintf(
            '<?php
$coverage = new SebastianBergmann\CodeCoverage\CodeCoverage;
$coverage->setData(%s);
$coverage->setTests(%s);

$filter = $coverage->filter();
$filter->setWhitelistedFiles(%s);

return $coverage;',
            var_export($coverage->getData(true), 1),
            var_export($coverage->getTests(), 1),
            var_export($filter->getWhitelistedFiles(), 1)
        );

        if ($target !== null) {
            return file_put_contents($target, $output);
        } else {
            return $output;
        }
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report;

use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\CodeCoverage\Util;

/**
 * Generates human readable output from a code coverage object.
 *
 * The output gets put into a text file our written to the CLI.
 */
class Text
{
    private $lowUpperBound;
    private $highLowerBound;
    private $showUncoveredFiles;
    private $showOnlySummary;

    private $colors = [
        'green'  => "\x1b[30;42m",
        'yellow' => "\x1b[30;43m",
        'red'    => "\x1b[37;41m",
        'header' => "\x1b[1;37;40m",
        'reset'  => "\x1b[0m",
        'eol'    => "\x1b[2K",
    ];

    /**
     * @param int  $lowUpperBound
     * @param int  $highLowerBound
     * @param bool $showUncoveredFiles
     * @param bool $showOnlySummary
     */
    public function __construct($lowUpperBound = 50, $highLowerBound = 90, $showUncoveredFiles = false, $showOnlySummary = false)
    {
        $this->lowUpperBound      = $lowUpperBound;
        $this->highLowerBound     = $highLowerBound;
        $this->showUncoveredFiles = $showUncoveredFiles;
        $this->showOnlySummary    = $showOnlySummary;
    }

    /**
     * @param CodeCoverage $coverage
     * @param bool         $showColors
     *
     * @return string
     */
    public function process(CodeCoverage $coverage, $showColors = false)
    {
        $output = PHP_EOL . PHP_EOL;
        $report = $coverage->getReport();
        unset($coverage);

        $colors = [
            'header'  => '',
            'classes' => '',
            'methods' => '',
            'lines'   => '',
            'reset'   => '',
            'eol'     => ''
        ];

        if ($showColors) {
            $colors['classes'] = $this->getCoverageColor(
                $report->getNumTestedClassesAndTraits(),
                $report->getNumClassesAndTraits()
            );
            $colors['methods'] = $this->getCoverageColor(
                $report->getNumTestedMethods(),
                $report->getNumMethods()
            );
            $colors['lines']   = $this->getCoverageColor(
                $report->getNumExecutedLines(),
                $report->getNumExecutableLines()
            );
            $colors['reset']   = $this->colors['reset'];
            $colors['header']  = $this->colors['header'];
            $colors['eol']     = $this->colors['eol'];
        }

        $classes = sprintf(
            '  Classes: %6s (%d/%d)',
            Util::percent(
                $report->getNumTestedClassesAndTraits(),
                $report->getNumClassesAndTraits(),
                true
            ),
            $report->getNumTestedClassesAndTraits(),
            $report->getNumClassesAndTraits()
        );

        $methods = sprintf(
            '  Methods: %6s (%d/%d)',
            Util::percent(
                $report->getNumTestedMethods(),
                $report->getNumMethods(),
                true
            ),
            $report->getNumTestedMethods(),
            $report->getNumMethods()
        );

        $lines = sprintf(
            '  Lines:   %6s (%d/%d)',
            Util::percent(
                $report->getNumExecutedLines(),
                $report->getNumExecutableLines(),
                true
            ),
            $report->getNumExecutedLines(),
            $report->getNumExecutableLines()
        );

        $padding = max(array_map('strlen', [$classes, $methods, $lines]));

        if ($this->showOnlySummary) {
            $title   = 'Code Coverage Report Summary:';
            $padding = max($padding, strlen($title));

            $output .= $this->format($colors['header'], $padding, $title);
        } else {
            $date  = date('  Y-m-d H:i:s', $_SERVER['REQUEST_TIME']);
            $title = 'Code Coverage Report:';

            $output .= $this->format($colors['header'], $padding, $title);
            $output .= $this->format($colors['header'], $padding, $date);
            $output .= $this->format($colors['header'], $padding, '');
            $output .= $this->format($colors['header'], $padding, ' Summary:');
        }

        $output .= $this->format($colors['classes'], $padding, $classes);
        $output .= $this->format($colors['methods'], $padding, $methods);
        $output .= $this->format($colors['lines'], $padding, $lines);

        if ($this->showOnlySummary) {
            return $output . PHP_EOL;
        }

        $classCoverage = [];

        foreach ($report as $item) {
            if (!$item instanceof File) {
                continue;
            }

            $classes = $item->getClassesAndTraits();

            foreach ($classes as $className => $class) {
                $classStatements        = 0;
                $coveredClassStatements = 0;
                $coveredMethods         = 0;
                $classMethods           = 0;

                foreach ($class['methods'] as $method) {
                    if ($method['executableLines'] == 0) {
                        continue;
                    }

                    $classMethods++;
                    $classStatements        += $method['executableLines'];
                    $coveredClassStatements += $method['executedLines'];
                    if ($method['coverage'] == 100) {
                        $coveredMethods++;
                    }
                }

                if (!empty($class['package']['namespace'])) {
                    $namespace = '\\' . $class['package']['namespace'] . '::';
                } elseif (!empty($class['package']['fullPackage'])) {
                    $namespace = '@' . $class['package']['fullPackage'] . '::';
                } else {
                    $namespace = '';
                }

                $classCoverage[$namespace . $className] = [
                    'namespace'         => $namespace,
                    'className '        => $className,
                    'methodsCovered'    => $coveredMethods,
                    'methodCount'       => $classMethods,
                    'statementsCovered' => $coveredClassStatements,
                    'statementCount'    => $classStatements,
                ];
            }
        }

        ksort($classCoverage);

        $methodColor = '';
        $linesColor  = '';
        $resetColor  = '';

        foreach ($classCoverage as $fullQualifiedPath => $classInfo) {
            if ($classInfo['statementsCovered'] != 0 ||
                $this->showUncoveredFiles) {
                if ($showColors) {
                    $methodColor = $this->getCoverageColor($classInfo['methodsCovered'], $classInfo['methodCount']);
                    $linesColor  = $this->getCoverageColor($classInfo['statementsCovered'], $classInfo['statementCount']);
                    $resetColor  = $colors['reset'];
                }

                $output .= PHP_EOL . $fullQualifiedPath . PHP_EOL
                    . '  ' . $methodColor . 'Methods: ' . $this->printCoverageCounts($classInfo['methodsCovered'], $classInfo['methodCount'], 2) . $resetColor . ' '
                    . '  ' . $linesColor . 'Lines: ' . $this->printCoverageCounts($classInfo['statementsCovered'], $classInfo['statementCount'], 3) . $resetColor
                ;
            }
        }

        return $output . PHP_EOL;
    }

    protected function getCoverageColor($numberOfCoveredElements, $totalNumberOfElements)
    {
        $coverage = Util::percent(
            $numberOfCoveredElements,
            $totalNumberOfElements
        );

        if ($coverage >= $this->highLowerBound) {
            return $this->colors['green'];
        } elseif ($coverage > $this->lowUpperBound) {
            return $this->colors['yellow'];
        }

        return $this->colors['red'];
    }

    protected function printCoverageCounts($numberOfCoveredElements, $totalNumberOfElements, $precision)
    {
        $format = '%' . $precision . 's';

        return Util::percent(
            $numberOfCoveredElements,
            $totalNumberOfElements,
            true,
            true
        ) .
        ' (' . sprintf($format, $numberOfCoveredElements) . '/' .
        sprintf($format, $totalNumberOfElements) . ')';
    }

    private function format($color, $padding, $string)
    {
        $reset = $color ? $this->colors['reset'] : '';

        return $color . str_pad($string, $padding) . $reset . PHP_EOL;
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report\Xml;

use SebastianBergmann\CodeCoverage\RuntimeException;

class Coverage
{
    /**
     * @var \XMLWriter
     */
    private $writer;

    /**
     * @var \DOMElement
     */
    private $contextNode;

    /**
     * @var bool
     */
    private $finalized = false;

    public function __construct(\DOMElement $context, $line)
    {
        $this->contextNode = $context;

        $this->writer = new \XMLWriter;
        $this->writer->openMemory();
        $this->writer->startElementNs(null, $context->nodeName, 'http://schema.phpunit.de/coverage/1.0');
        $this->writer->writeAttribute('nr', $line);
    }

    public function addTest($test)
    {
        if ($this->finalized) {
            throw new RuntimeException('Coverage Report already finalized');
        }

        $this->writer->startElement('covered');
        $this->writer->writeAttribute('by', $test);
        $this->writer->endElement();
    }

    public function finalize()
    {
        $this->writer->endElement();

        $fragment = $this->contextNode->ownerDocument->createDocumentFragment();
        $fragment->appendXML($this->writer->outputMemory());

        $this->contextNode->parentNode->replaceChild(
            $fragment,
            $this->contextNode
        );

        $this->finalized = true;
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report\Xml;

class Directory extends Node
{
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report\Xml;

use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Node\AbstractNode;
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
use SebastianBergmann\CodeCoverage\Node\File as FileNode;
use SebastianBergmann\CodeCoverage\RuntimeException;

class Facade
{
    /**
     * @var string
     */
    private $target;

    /**
     * @var Project
     */
    private $project;

    /**
     * @param CodeCoverage $coverage
     * @param string       $target
     *
     * @throws RuntimeException
     */
    public function process(CodeCoverage $coverage, $target)
    {
        if (substr($target, -1, 1) != DIRECTORY_SEPARATOR) {
            $target .= DIRECTORY_SEPARATOR;
        }

        $this->target = $target;
        $this->initTargetDirectory($target);

        $report = $coverage->getReport();

        $this->project = new Project(
            $coverage->getReport()->getName()
        );

        $this->processTests($coverage->getTests());
        $this->processDirectory($report, $this->project);

        $index                     = $this->project->asDom();
        $index->formatOutput       = true;
        $index->preserveWhiteSpace = false;
        $index->save($target . '/index.xml');
    }

    /**
     * @param string $directory
     */
    private function initTargetDirectory($directory)
    {
        if (file_exists($directory)) {
            if (!is_dir($directory)) {
                throw new RuntimeException(
                    "'$directory' exists but is not a directory."
                );
            }

            if (!is_writable($directory)) {
                throw new RuntimeException(
                    "'$directory' exists but is not writable."
                );
            }
        } elseif (!@mkdir($directory, 0777, true)) {
            throw new RuntimeException(
                "'$directory' could not be created."
            );
        }
    }

    private function processDirectory(DirectoryNode $directory, Node $context)
    {
        $dirObject = $context->addDirectory($directory->getName());

        $this->setTotals($directory, $dirObject->getTotals());

        foreach ($directory->getDirectories() as $node) {
            $this->processDirectory($node, $dirObject);
        }

        foreach ($directory->getFiles() as $node) {
            $this->processFile($node, $dirObject);
        }
    }

    private function processFile(FileNode $file, Directory $context)
    {
        $fileObject = $context->addFile(
            $file->getName(),
            $file->getId() . '.xml'
        );

        $this->setTotals($file, $fileObject->getTotals());

        $fileReport = new Report($file->getName());

        $this->setTotals($file, $fileReport->getTotals());

        foreach ($file->getClassesAndTraits() as $unit) {
            $this->processUnit($unit, $fileReport);
        }

        foreach ($file->getFunctions() as $function) {
            $this->processFunction($function, $fileReport);
        }

        foreach ($file->getCoverageData() as $line => $tests) {
            if (!is_array($tests) || count($tests) == 0) {
                continue;
            }

            $coverage = $fileReport->getLineCoverage($line);

            foreach ($tests as $test) {
                $coverage->addTest($test);
            }

            $coverage->finalize();
        }

        $this->initTargetDirectory(
            $this->target . dirname($file->getId()) . '/'
        );

        $fileDom                     = $fileReport->asDom();
        $fileDom->formatOutput       = true;
        $fileDom->preserveWhiteSpace = false;
        $fileDom->save($this->target . $file->getId() . '.xml');
    }

    private function processUnit($unit, Report $report)
    {
        if (isset($unit['className'])) {
            $unitObject = $report->getClassObject($unit['className']);
        } else {
            $unitObject = $report->getTraitObject($unit['traitName']);
        }

        $unitObject->setLines(
            $unit['startLine'],
            $unit['executableLines'],
            $unit['executedLines']
        );

        $unitObject->setCrap($unit['crap']);

        $unitObject->setPackage(
            $unit['package']['fullPackage'],
            $unit['package']['package'],
            $unit['package']['subpackage'],
            $unit['package']['category']
        );

        $unitObject->setNamespace($unit['package']['namespace']);

        foreach ($unit['methods'] as $method) {
            $methodObject = $unitObject->addMethod($method['methodName']);
            $methodObject->setSignature($method['signature']);
            $methodObject->setLines($method['startLine'], $method['endLine']);
            $methodObject->setCrap($method['crap']);
            $methodObject->setTotals(
                $method['executableLines'],
                $method['executedLines'],
                $method['coverage']
            );
        }
    }

    private function processFunction($function, Report $report)
    {
        $functionObject = $report->getFunctionObject($function['functionName']);

        $functionObject->setSignature($function['signature']);
        $functionObject->setLines($function['startLine']);
        $functionObject->setCrap($function['crap']);
        $functionObject->setTotals($function['executableLines'], $function['executedLines'], $function['coverage']);
    }

    private function processTests(array $tests)
    {
        $testsObject = $this->project->getTests();

        foreach ($tests as $test => $result) {
            if ($test == 'UNCOVERED_FILES_FROM_WHITELIST') {
                continue;
            }

            $testsObject->addTest($test, $result);
        }
    }

    private function setTotals(AbstractNode $node, Totals $totals)
    {
        $loc = $node->getLinesOfCode();

        $totals->setNumLines(
            $loc['loc'],
            $loc['cloc'],
            $loc['ncloc'],
            $node->getNumExecutableLines(),
            $node->getNumExecutedLines()
        );

        $totals->setNumClasses(
            $node->getNumClasses(),
            $node->getNumTestedClasses()
        );

        $totals->setNumTraits(
            $node->getNumTraits(),
            $node->getNumTestedTraits()
        );

        $totals->setNumMethods(
            $node->getNumMethods(),
            $node->getNumTestedMethods()
        );

        $totals->setNumFunctions(
            $node->getNumFunctions(),
            $node->getNumTestedFunctions()
        );
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report\Xml;

class File
{
    /**
     * @var \DOMDocument
     */
    protected $dom;

    /**
     * @var \DOMElement
     */
    protected $contextNode;

    public function __construct(\DOMElement $context)
    {
        $this->dom         = $context->ownerDocument;
        $this->contextNode = $context;
    }

    public function getTotals()
    {
        $totalsContainer = $this->contextNode->firstChild;

        if (!$totalsContainer) {
            $totalsContainer = $this->contextNode->appendChild(
                $this->dom->createElementNS(
                    'http://schema.phpunit.de/coverage/1.0',
                    'totals'
                )
            );
        }

        return new Totals($totalsContainer);
    }

    public function getLineCoverage($line)
    {
        $coverage = $this->contextNode->getElementsByTagNameNS(
            'http://schema.phpunit.de/coverage/1.0',
            'coverage'
        )->item(0);

        if (!$coverage) {
            $coverage = $this->contextNode->appendChild(
                $this->dom->createElementNS(
                    'http://schema.phpunit.de/coverage/1.0',
                    'coverage'
                )
            );
        }

        $lineNode = $coverage->appendChild(
            $this->dom->createElementNS(
                'http://schema.phpunit.de/coverage/1.0',
                'line'
            )
        );

        return new Coverage($lineNode, $line);
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report\Xml;

class Method
{
    /**
     * @var \DOMElement
     */
    private $contextNode;

    public function __construct(\DOMElement $context, $name)
    {
        $this->contextNode = $context;

        $this->setName($name);
    }

    private function setName($name)
    {
        $this->contextNode->setAttribute('name', $name);
    }

    public function setSignature($signature)
    {
        $this->contextNode->setAttribute('signature', $signature);
    }

    public function setLines($start, $end = null)
    {
        $this->contextNode->setAttribute('start', $start);

        if ($end !== null) {
            $this->contextNode->setAttribute('end', $end);
        }
    }

    public function setTotals($executable, $executed, $coverage)
    {
        $this->contextNode->setAttribute('executable', $executable);
        $this->contextNode->setAttribute('executed', $executed);
        $this->contextNode->setAttribute('coverage', $coverage);
    }

    public function setCrap($crap)
    {
        $this->contextNode->setAttribute('crap', $crap);
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report\Xml;

class Node
{
    /**
     * @var \DOMDocument
     */
    private $dom;

    /**
     * @var \DOMElement
     */
    private $contextNode;

    public function __construct(\DOMElement $context)
    {
        $this->setContextNode($context);
    }

    protected function setContextNode(\DOMElement $context)
    {
        $this->dom         = $context->ownerDocument;
        $this->contextNode = $context;
    }

    public function getDom()
    {
        return $this->dom;
    }

    protected function getContextNode()
    {
        return $this->contextNode;
    }

    public function getTotals()
    {
        $totalsContainer = $this->getContextNode()->firstChild;

        if (!$totalsContainer) {
            $totalsContainer = $this->getContextNode()->appendChild(
                $this->dom->createElementNS(
                    'http://schema.phpunit.de/coverage/1.0',
                    'totals'
                )
            );
        }

        return new Totals($totalsContainer);
    }

    public function addDirectory($name)
    {
        $dirNode = $this->getDom()->createElementNS(
            'http://schema.phpunit.de/coverage/1.0',
            'directory'
        );

        $dirNode->setAttribute('name', $name);
        $this->getContextNode()->appendChild($dirNode);

        return new Directory($dirNode);
    }

    public function addFile($name, $href)
    {
        $fileNode = $this->getDom()->createElementNS(
            'http://schema.phpunit.de/coverage/1.0',
            'file'
        );

        $fileNode->setAttribute('name', $name);
        $fileNode->setAttribute('href', $href);
        $this->getContextNode()->appendChild($fileNode);

        return new File($fileNode);
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report\Xml;

class Project extends Node
{
    public function __construct($name)
    {
        $this->init();
        $this->setProjectName($name);
    }

    private function init()
    {
        $dom = new \DOMDocument;
        $dom->loadXML('<?xml version="1.0" ?><phpunit xmlns="http://schema.phpunit.de/coverage/1.0"><project/></phpunit>');

        $this->setContextNode(
            $dom->getElementsByTagNameNS(
                'http://schema.phpunit.de/coverage/1.0',
                'project'
            )->item(0)
        );
    }

    private function setProjectName($name)
    {
        $this->getContextNode()->setAttribute('name', $name);
    }

    public function getTests()
    {
        $testsNode = $this->getContextNode()->getElementsByTagNameNS(
            'http://schema.phpunit.de/coverage/1.0',
            'tests'
        )->item(0);

        if (!$testsNode) {
            $testsNode = $this->getContextNode()->appendChild(
                $this->getDom()->createElementNS(
                    'http://schema.phpunit.de/coverage/1.0',
                    'tests'
                )
            );
        }

        return new Tests($testsNode);
    }

    public function asDom()
    {
        return $this->getDom();
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report\Xml;

class Report extends File
{
    public function __construct($name)
    {
        $this->dom = new \DOMDocument;
        $this->dom->loadXML('<?xml version="1.0" ?><phpunit xmlns="http://schema.phpunit.de/coverage/1.0"><file /></phpunit>');

        $this->contextNode = $this->dom->getElementsByTagNameNS(
            'http://schema.phpunit.de/coverage/1.0',
            'file'
        )->item(0);

        $this->setName($name);
    }

    private function setName($name)
    {
        $this->contextNode->setAttribute('name', $name);
    }

    public function asDom()
    {
        return $this->dom;
    }

    public function getFunctionObject($name)
    {
        $node = $this->contextNode->appendChild(
            $this->dom->createElementNS(
                'http://schema.phpunit.de/coverage/1.0',
                'function'
            )
        );

        return new Method($node, $name);
    }

    public function getClassObject($name)
    {
        return $this->getUnitObject('class', $name);
    }

    public function getTraitObject($name)
    {
        return $this->getUnitObject('trait', $name);
    }

    private function getUnitObject($tagName, $name)
    {
        $node = $this->contextNode->appendChild(
            $this->dom->createElementNS(
                'http://schema.phpunit.de/coverage/1.0',
                $tagName
            )
        );

        return new Unit($node, $name);
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report\Xml;

class Tests
{
    private $contextNode;

    private $codeMap = [
        0 => 'PASSED',     // PHPUnit_Runner_BaseTestRunner::STATUS_PASSED
        1 => 'SKIPPED',    // PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED
        2 => 'INCOMPLETE', // PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE
        3 => 'FAILURE',    // PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE
        4 => 'ERROR',      // PHPUnit_Runner_BaseTestRunner::STATUS_ERROR
        5 => 'RISKY',      // PHPUnit_Runner_BaseTestRunner::STATUS_RISKY
        6 => 'WARNING'     // PHPUnit_Runner_BaseTestRunner::STATUS_WARNING
    ];

    public function __construct(\DOMElement $context)
    {
        $this->contextNode = $context;
    }

    public function addTest($test, array $result)
    {
        $node = $this->contextNode->appendChild(
            $this->contextNode->ownerDocument->createElementNS(
                'http://schema.phpunit.de/coverage/1.0',
                'test'
            )
        );

        $node->setAttribute('name', $test);
        $node->setAttribute('size', $result['size']);
        $node->setAttribute('result', (int) $result['status']);
        $node->setAttribute('status', $this->codeMap[(int) $result['status']]);
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report\Xml;

use SebastianBergmann\CodeCoverage\Util;

class Totals
{
    /**
     * @var \DOMNode
     */
    private $container;

    /**
     * @var \DOMElement
     */
    private $linesNode;

    /**
     * @var \DOMElement
     */
    private $methodsNode;

    /**
     * @var \DOMElement
     */
    private $functionsNode;

    /**
     * @var \DOMElement
     */
    private $classesNode;

    /**
     * @var \DOMElement
     */
    private $traitsNode;

    public function __construct(\DOMElement $container)
    {
        $this->container = $container;
        $dom             = $container->ownerDocument;

        $this->linesNode = $dom->createElementNS(
            'http://schema.phpunit.de/coverage/1.0',
            'lines'
        );

        $this->methodsNode = $dom->createElementNS(
            'http://schema.phpunit.de/coverage/1.0',
            'methods'
        );

        $this->functionsNode = $dom->createElementNS(
            'http://schema.phpunit.de/coverage/1.0',
            'functions'
        );

        $this->classesNode = $dom->createElementNS(
            'http://schema.phpunit.de/coverage/1.0',
            'classes'
        );

        $this->traitsNode = $dom->createElementNS(
            'http://schema.phpunit.de/coverage/1.0',
            'traits'
        );

        $container->appendChild($this->linesNode);
        $container->appendChild($this->methodsNode);
        $container->appendChild($this->functionsNode);
        $container->appendChild($this->classesNode);
        $container->appendChild($this->traitsNode);
    }

    public function getContainer()
    {
        return $this->container;
    }

    public function setNumLines($loc, $cloc, $ncloc, $executable, $executed)
    {
        $this->linesNode->setAttribute('total', $loc);
        $this->linesNode->setAttribute('comments', $cloc);
        $this->linesNode->setAttribute('code', $ncloc);
        $this->linesNode->setAttribute('executable', $executable);
        $this->linesNode->setAttribute('executed', $executed);
        $this->linesNode->setAttribute(
            'percent',
            Util::percent($executed, $executable, true)
        );
    }

    public function setNumClasses($count, $tested)
    {
        $this->classesNode->setAttribute('count', $count);
        $this->classesNode->setAttribute('tested', $tested);
        $this->classesNode->setAttribute(
            'percent',
            Util::percent($tested, $count, true)
        );
    }

    public function setNumTraits($count, $tested)
    {
        $this->traitsNode->setAttribute('count', $count);
        $this->traitsNode->setAttribute('tested', $tested);
        $this->traitsNode->setAttribute(
            'percent',
            Util::percent($tested, $count, true)
        );
    }

    public function setNumMethods($count, $tested)
    {
        $this->methodsNode->setAttribute('count', $count);
        $this->methodsNode->setAttribute('tested', $tested);
        $this->methodsNode->setAttribute(
            'percent',
            Util::percent($tested, $count, true)
        );
    }

    public function setNumFunctions($count, $tested)
    {
        $this->functionsNode->setAttribute('count', $count);
        $this->functionsNode->setAttribute('tested', $tested);
        $this->functionsNode->setAttribute(
            'percent',
            Util::percent($tested, $count, true)
        );
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage\Report\Xml;

class Unit
{
    /**
     * @var \DOMElement
     */
    private $contextNode;

    public function __construct(\DOMElement $context, $name)
    {
        $this->contextNode = $context;

        $this->setName($name);
    }

    private function setName($name)
    {
        $this->contextNode->setAttribute('name', $name);
    }

    public function setLines($start, $executable, $executed)
    {
        $this->contextNode->setAttribute('start', $start);
        $this->contextNode->setAttribute('executable', $executable);
        $this->contextNode->setAttribute('executed', $executed);
    }

    public function setCrap($crap)
    {
        $this->contextNode->setAttribute('crap', $crap);
    }

    public function setPackage($full, $package, $sub, $category)
    {
        $node = $this->contextNode->getElementsByTagNameNS(
            'http://schema.phpunit.de/coverage/1.0',
            'package'
        )->item(0);

        if (!$node) {
            $node = $this->contextNode->appendChild(
                $this->contextNode->ownerDocument->createElementNS(
                    'http://schema.phpunit.de/coverage/1.0',
                    'package'
                )
            );
        }

        $node->setAttribute('full', $full);
        $node->setAttribute('name', $package);
        $node->setAttribute('sub', $sub);
        $node->setAttribute('category', $category);
    }

    public function setNamespace($namespace)
    {
        $node = $this->contextNode->getElementsByTagNameNS(
            'http://schema.phpunit.de/coverage/1.0',
            'namespace'
        )->item(0);

        if (!$node) {
            $node = $this->contextNode->appendChild(
                $this->contextNode->ownerDocument->createElementNS(
                    'http://schema.phpunit.de/coverage/1.0',
                    'namespace'
                )
            );
        }

        $node->setAttribute('name', $namespace);
    }

    public function addMethod($name)
    {
        $node = $this->contextNode->appendChild(
            $this->contextNode->ownerDocument->createElementNS(
                'http://schema.phpunit.de/coverage/1.0',
                'method'
            )
        );

        return new Method($node, $name);
    }
}
<?php
/*
 * This file is part of the php-code-coverage package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeCoverage;

/**
 * Utility methods.
 */
class Util
{
    /**
     * @param float $a
     * @param float $b
     * @param bool  $asString
     * @param bool  $fixedWidth
     *
     * @return float|int|string
     */
    public static function percent($a, $b, $asString = false, $fixedWidth = false)
    {
        if ($asString && $b == 0) {
            return '';
        }

        if ($b > 0) {
            $percent = ($a / $b) * 100;
        } else {
            $percent = 100;
        }

        if ($asString) {
            if ($fixedWidth) {
                return sprintf('%6.2F%%', $percent);
            }

            return sprintf('%01.2F%%', $percent);
        } else {
            return $percent;
        }
    }
}
<?php
/*
 * This file is part of the File_Iterator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Façade implementation that uses File_Iterator_Factory to create a
 * File_Iterator that operates on an AppendIterator that contains an
 * RecursiveDirectoryIterator for each given path. The list of unique
 * files is returned as an array.
 *
 * @since     Class available since Release 1.3.0
 */
class File_Iterator_Facade
{
    /**
     * @param  array|string $paths
     * @param  array|string $suffixes
     * @param  array|string $prefixes
     * @param  array        $exclude
     * @param  bool         $commonPath
     * @return array
     */
    public function getFilesAsArray($paths, $suffixes = '', $prefixes = '', array $exclude = array(), $commonPath = FALSE)
    {
        if (is_string($paths)) {
            $paths = array($paths);
        }

        $factory  = new File_Iterator_Factory;
        $iterator = $factory->getFileIterator(
          $paths, $suffixes, $prefixes, $exclude
        );

        $files = array();

        foreach ($iterator as $file) {
            $file = $file->getRealPath();

            if ($file) {
                $files[] = $file;
            }
        }

        foreach ($paths as $path) {
            if (is_file($path)) {
                $files[] = realpath($path);
            }
        }

        $files = array_unique($files);
        sort($files);

        if ($commonPath) {
            return array(
              'commonPath' => $this->getCommonPath($files),
              'files'      => $files
            );
        } else {
            return $files;
        }
    }

    /**
     * Returns the common path of a set of files.
     *
     * @param  array  $files
     * @return string
     */
    protected function getCommonPath(array $files)
    {
        $count = count($files);

        if ($count == 0) {
            return '';
        }

        if ($count == 1) {
            return dirname($files[0]) . DIRECTORY_SEPARATOR;
        }

        $_files = array();

        foreach ($files as $file) {
            $_files[] = $_fileParts = explode(DIRECTORY_SEPARATOR, $file);

            if (empty($_fileParts[0])) {
                $_fileParts[0] = DIRECTORY_SEPARATOR;
            }
        }

        $common = '';
        $done   = FALSE;
        $j      = 0;
        $count--;

        while (!$done) {
            for ($i = 0; $i < $count; $i++) {
                if ($_files[$i][$j] != $_files[$i+1][$j]) {
                    $done = TRUE;
                    break;
                }
            }

            if (!$done) {
                $common .= $_files[0][$j];

                if ($j > 0) {
                    $common .= DIRECTORY_SEPARATOR;
                }
            }

            $j++;
        }

        return DIRECTORY_SEPARATOR . $common;
    }
}
<?php
/*
 * This file is part of the File_Iterator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Factory Method implementation that creates a File_Iterator that operates on
 * an AppendIterator that contains an RecursiveDirectoryIterator for each given
 * path.
 *
 * @since     Class available since Release 1.1.0
 */
class File_Iterator_Factory
{
    /**
     * @param  array|string   $paths
     * @param  array|string   $suffixes
     * @param  array|string   $prefixes
     * @param  array          $exclude
     * @return AppendIterator
     */
    public function getFileIterator($paths, $suffixes = '', $prefixes = '', array $exclude = array())
    {
        if (is_string($paths)) {
            $paths = array($paths);
        }

        $paths   = $this->getPathsAfterResolvingWildcards($paths);
        $exclude = $this->getPathsAfterResolvingWildcards($exclude);

        if (is_string($prefixes)) {
            if ($prefixes != '') {
                $prefixes = array($prefixes);
            } else {
                $prefixes = array();
            }
        }

        if (is_string($suffixes)) {
            if ($suffixes != '') {
                $suffixes = array($suffixes);
            } else {
                $suffixes = array();
            }
        }

        $iterator = new AppendIterator;

        foreach ($paths as $path) {
            if (is_dir($path)) {
                $iterator->append(
                  new File_Iterator(
                    new RecursiveIteratorIterator(
                      new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::FOLLOW_SYMLINKS)
                    ),
                    $suffixes,
                    $prefixes,
                    $exclude,
                    $path
                  )
                );
            }
        }

        return $iterator;
    }

    /**
     * @param  array $paths
     * @return array
     */
    protected function getPathsAfterResolvingWildcards(array $paths)
    {
        $_paths = array();

        foreach ($paths as $path) {
            if ($locals = glob($path, GLOB_ONLYDIR)) {
                $_paths = array_merge($_paths, $locals);
            } else {
                $_paths[] = $path;
            }
        }

        return $_paths;
    }
}
<?php
/*
 * This file is part of the File_Iterator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * FilterIterator implementation that filters files based on prefix(es) and/or
 * suffix(es). Hidden files and files from hidden directories are also filtered.
 *
 * @since     Class available since Release 1.0.0
 */
class File_Iterator extends FilterIterator
{
    const PREFIX = 0;
    const SUFFIX = 1;

    /**
     * @var array
     */
    protected $suffixes = array();

    /**
     * @var array
     */
    protected $prefixes = array();

    /**
     * @var array
     */
    protected $exclude = array();

    /**
     * @var string
     */
    protected $basepath;

    /**
     * @param Iterator $iterator
     * @param array    $suffixes
     * @param array    $prefixes
     * @param array    $exclude
     * @param string   $basepath
     */
    public function __construct(Iterator $iterator, array $suffixes = array(), array $prefixes = array(), array $exclude = array(), $basepath = NULL)
    {
        $exclude = array_filter(array_map('realpath', $exclude));

        if ($basepath !== NULL) {
            $basepath = realpath($basepath);
        }

        if ($basepath === FALSE) {
            $basepath = NULL;
        } else {
            foreach ($exclude as &$_exclude) {
                $_exclude = str_replace($basepath, '', $_exclude);
            }
        }

        $this->prefixes = $prefixes;
        $this->suffixes = $suffixes;
        $this->exclude  = $exclude;
        $this->basepath = $basepath;

        parent::__construct($iterator);
    }

    /**
     * @return bool
     */
    public function accept()
    {
        $current  = $this->getInnerIterator()->current();
        $filename = $current->getFilename();
        $realpath = $current->getRealPath();

        if ($this->basepath !== NULL) {
            $realpath = str_replace($this->basepath, '', $realpath);
        }

        // Filter files in hidden directories.
        if (preg_match('=/\.[^/]*/=', $realpath)) {
            return FALSE;
        }

        return $this->acceptPath($realpath) &&
               $this->acceptPrefix($filename) &&
               $this->acceptSuffix($filename);
    }

    /**
     * @param  string $path
     * @return bool
     * @since  Method available since Release 1.1.0
     */
    protected function acceptPath($path)
    {
        foreach ($this->exclude as $exclude) {
            if (strpos($path, $exclude) === 0) {
                return FALSE;
            }
        }

        return TRUE;
    }

    /**
     * @param  string $filename
     * @return bool
     * @since  Method available since Release 1.1.0
     */
    protected function acceptPrefix($filename)
    {
        return $this->acceptSubString($filename, $this->prefixes, self::PREFIX);
    }

    /**
     * @param  string $filename
     * @return bool
     * @since  Method available since Release 1.1.0
     */
    protected function acceptSuffix($filename)
    {
        return $this->acceptSubString($filename, $this->suffixes, self::SUFFIX);
    }

    /**
     * @param  string $filename
     * @param  array  $subString
     * @param  int    $type
     * @return bool
     * @since  Method available since Release 1.1.0
     */
    protected function acceptSubString($filename, array $subStrings, $type)
    {
        if (empty($subStrings)) {
            return TRUE;
        }

        $matched = FALSE;

        foreach ($subStrings as $string) {
            if (($type == self::PREFIX && strpos($filename, $string) === 0) ||
                ($type == self::SUFFIX &&
                 substr($filename, -1 * strlen($string)) == $string)) {
                $matched = TRUE;
                break;
            }
        }

        return $matched;
    }
}
<?php
/*
 * This file is part of the Text_Template package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A simple template engine.
 *
 * @since Class available since Release 1.0.0
 */
class Text_Template
{
    /**
     * @var string
     */
    protected $template = '';

    /**
     * @var string
     */
    protected $openDelimiter = '{';

    /**
     * @var string
     */
    protected $closeDelimiter = '}';

    /**
     * @var array
     */
    protected $values = array();

    /**
     * Constructor.
     *
     * @param  string                   $file
     * @throws InvalidArgumentException
     */
    public function __construct($file = '', $openDelimiter = '{', $closeDelimiter = '}')
    {
        $this->setFile($file);
        $this->openDelimiter  = $openDelimiter;
        $this->closeDelimiter = $closeDelimiter;
    }

    /**
     * Sets the template file.
     *
     * @param  string                   $file
     * @throws InvalidArgumentException
     */
    public function setFile($file)
    {
        $distFile = $file . '.dist';

        if (file_exists($file)) {
            $this->template = file_get_contents($file);
        }

        else if (file_exists($distFile)) {
            $this->template = file_get_contents($distFile);
        }

        else {
            throw new InvalidArgumentException(
              'Template file could not be loaded.'
            );
        }
    }

    /**
     * Sets one or more template variables.
     *
     * @param array $values
     * @param bool  $merge
     */
    public function setVar(array $values, $merge = TRUE)
    {
        if (!$merge || empty($this->values)) {
            $this->values = $values;
        } else {
            $this->values = array_merge($this->values, $values);
        }
    }

    /**
     * Renders the template and returns the result.
     *
     * @return string
     */
    public function render()
    {
        $keys = array();

        foreach ($this->values as $key => $value) {
            $keys[] = $this->openDelimiter . $key . $this->closeDelimiter;
        }

        return str_replace($keys, $this->values, $this->template);
    }

    /**
     * Renders the template and writes the result to a file.
     *
     * @param string $target
     */
    public function renderTo($target)
    {
        $fp = @fopen($target, 'wt');

        if ($fp) {
            fwrite($fp, $this->render());
            fclose($fp);
        } else {
            $error = error_get_last();

            throw new RuntimeException(
              sprintf(
                'Could not write to %s: %s',
                $target,
                substr(
                  $error['message'],
                  strpos($error['message'], ':') + 2
                )
              )
            );
        }
    }
}

<?php
/*
 * This file is part of the PHP_Timer package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Utility class for timing.
 */
class PHP_Timer
{
    /**
     * @var array
     */
    private static $times = array(
      'hour'   => 3600000,
      'minute' => 60000,
      'second' => 1000
    );

    /**
     * @var array
     */
    private static $startTimes = array();

    /**
     * @var float
     */
    public static $requestTime;

    /**
     * Starts the timer.
     */
    public static function start()
    {
        array_push(self::$startTimes, microtime(true));
    }

    /**
     * Stops the timer and returns the elapsed time.
     *
     * @return float
     */
    public static function stop()
    {
        return microtime(true) - array_pop(self::$startTimes);
    }

    /**
     * Formats the elapsed time as a string.
     *
     * @param  float  $time
     * @return string
     */
    public static function secondsToTimeString($time)
    {
        $ms = round($time * 1000);

        foreach (self::$times as $unit => $value) {
            if ($ms >= $value) {
                $time = floor($ms / $value * 100.0) / 100.0;

                return $time . ' ' . ($time == 1 ? $unit : $unit . 's');
            }
        }

        return $ms . ' ms';
    }

    /**
     * Formats the elapsed time since the start of the request as a string.
     *
     * @return string
     */
    public static function timeSinceStartOfRequest()
    {
        return self::secondsToTimeString(microtime(true) - self::$requestTime);
    }

    /**
     * Returns the resources (time, memory) of the request as a string.
     *
     * @return string
     */
    public static function resourceUsage()
    {
        return sprintf(
            'Time: %s, Memory: %4.2fMB',
            self::timeSinceStartOfRequest(),
            memory_get_peak_usage(true) / 1048576
        );
    }
}

if (isset($_SERVER['REQUEST_TIME_FLOAT'])) {
    PHP_Timer::$requestTime = $_SERVER['REQUEST_TIME_FLOAT'];
} elseif (isset($_SERVER['REQUEST_TIME'])) {
    PHP_Timer::$requestTime = $_SERVER['REQUEST_TIME'];
} else {
    PHP_Timer::$requestTime = microtime(true);
}
<?php
/*
 * This file is part of the PHP_TokenStream package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A caching factory for token stream objects.
 *
 * @author    Sebastian Bergmann <sebastian@phpunit.de>
 * @copyright Sebastian Bergmann <sebastian@phpunit.de>
 * @license   http://www.opensource.org/licenses/BSD-3-Clause  The BSD 3-Clause License
 * @link      http://github.com/sebastianbergmann/php-token-stream/tree
 * @since     Class available since Release 1.0.0
 */
class PHP_Token_Stream_CachingFactory
{
    /**
     * @var array
     */
    protected static $cache = array();

    /**
     * @param  string $filename
     * @return PHP_Token_Stream
     */
    public static function get($filename)
    {
        if (!isset(self::$cache[$filename])) {
            self::$cache[$filename] = new PHP_Token_Stream($filename);
        }

        return self::$cache[$filename];
    }

    /**
     * @param string $filename
     */
    public static function clear($filename = null)
    {
        if (is_string($filename)) {
            unset(self::$cache[$filename]);
        } else {
            self::$cache = array();
        }
    }
}
<?php
/*
 * This file is part of the PHP_TokenStream package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A stream of PHP tokens.
 *
 * @author    Sebastian Bergmann <sebastian@phpunit.de>
 * @copyright Sebastian Bergmann <sebastian@phpunit.de>
 * @license   http://www.opensource.org/licenses/BSD-3-Clause  The BSD 3-Clause License
 * @link      http://github.com/sebastianbergmann/php-token-stream/tree
 * @since     Class available since Release 1.0.0
 */
class PHP_Token_Stream implements ArrayAccess, Countable, SeekableIterator
{
    /**
     * @var array
     */
    protected static $customTokens = array(
        '(' => 'PHP_Token_OPEN_BRACKET',
        ')' => 'PHP_Token_CLOSE_BRACKET',
        '[' => 'PHP_Token_OPEN_SQUARE',
        ']' => 'PHP_Token_CLOSE_SQUARE',
        '{' => 'PHP_Token_OPEN_CURLY',
        '}' => 'PHP_Token_CLOSE_CURLY',
        ';' => 'PHP_Token_SEMICOLON',
        '.' => 'PHP_Token_DOT',
        ',' => 'PHP_Token_COMMA',
        '=' => 'PHP_Token_EQUAL',
        '<' => 'PHP_Token_LT',
        '>' => 'PHP_Token_GT',
        '+' => 'PHP_Token_PLUS',
        '-' => 'PHP_Token_MINUS',
        '*' => 'PHP_Token_MULT',
        '/' => 'PHP_Token_DIV',
        '?' => 'PHP_Token_QUESTION_MARK',
        '!' => 'PHP_Token_EXCLAMATION_MARK',
        ':' => 'PHP_Token_COLON',
        '"' => 'PHP_Token_DOUBLE_QUOTES',
        '@' => 'PHP_Token_AT',
        '&' => 'PHP_Token_AMPERSAND',
        '%' => 'PHP_Token_PERCENT',
        '|' => 'PHP_Token_PIPE',
        '$' => 'PHP_Token_DOLLAR',
        '^' => 'PHP_Token_CARET',
        '~' => 'PHP_Token_TILDE',
        '`' => 'PHP_Token_BACKTICK'
    );

    /**
     * @var string
     */
    protected $filename;

    /**
     * @var array
     */
    protected $tokens = array();

    /**
     * @var integer
     */
    protected $position = 0;

    /**
     * @var array
     */
    protected $linesOfCode = array('loc' => 0, 'cloc' => 0, 'ncloc' => 0);

    /**
     * @var array
     */
    protected $classes;

    /**
     * @var array
     */
    protected $functions;

    /**
     * @var array
     */
    protected $includes;

    /**
     * @var array
     */
    protected $interfaces;

    /**
     * @var array
     */
    protected $traits;

    /**
     * @var array
     */
    protected $lineToFunctionMap = array();

    /**
     * Constructor.
     *
     * @param string $sourceCode
     */
    public function __construct($sourceCode)
    {
        if (is_file($sourceCode)) {
            $this->filename = $sourceCode;
            $sourceCode     = file_get_contents($sourceCode);
        }

        $this->scan($sourceCode);
    }

    /**
     * Destructor.
     */
    public function __destruct()
    {
        $this->tokens = array();
    }

    /**
     * @return string
     */
    public function __toString()
    {
        $buffer = '';

        foreach ($this as $token) {
            $buffer .= $token;
        }

        return $buffer;
    }

    /**
     * @return string
     * @since  Method available since Release 1.1.0
     */
    public function getFilename()
    {
        return $this->filename;
    }

    /**
     * Scans the source for sequences of characters and converts them into a
     * stream of tokens.
     *
     * @param string $sourceCode
     */
    protected function scan($sourceCode)
    {
        $id        = 0;
        $line      = 1;
        $tokens    = token_get_all($sourceCode);
        $numTokens = count($tokens);

        $lastNonWhitespaceTokenWasDoubleColon = false;

        for ($i = 0; $i < $numTokens; ++$i) {
            $token = $tokens[$i];
            $skip  = 0;

            if (is_array($token)) {
                $name = substr(token_name($token[0]), 2);
                $text = $token[1];

                if ($lastNonWhitespaceTokenWasDoubleColon && $name == 'CLASS') {
                    $name = 'CLASS_NAME_CONSTANT';
                } elseif ($name == 'USE' && isset($tokens[$i+2][0]) && $tokens[$i+2][0] == T_FUNCTION) {
                    $name = 'USE_FUNCTION';
                    $skip = 2;
                }

                $tokenClass = 'PHP_Token_' . $name;
            } else {
                $text       = $token;
                $tokenClass = self::$customTokens[$token];
            }

            $this->tokens[] = new $tokenClass($text, $line, $this, $id++);
            $lines          = substr_count($text, "\n");
            $line          += $lines;

            if ($tokenClass == 'PHP_Token_HALT_COMPILER') {
                break;
            } elseif ($tokenClass == 'PHP_Token_COMMENT' ||
                $tokenClass == 'PHP_Token_DOC_COMMENT') {
                $this->linesOfCode['cloc'] += $lines + 1;
            }

            if ($name == 'DOUBLE_COLON') {
                $lastNonWhitespaceTokenWasDoubleColon = true;
            } elseif ($name != 'WHITESPACE') {
                $lastNonWhitespaceTokenWasDoubleColon = false;
            }

            $i += $skip;
        }

        $this->linesOfCode['loc']   = substr_count($sourceCode, "\n");
        $this->linesOfCode['ncloc'] = $this->linesOfCode['loc'] -
                                      $this->linesOfCode['cloc'];
    }

    /**
     * @return integer
     */
    public function count()
    {
        return count($this->tokens);
    }

    /**
     * @return PHP_Token[]
     */
    public function tokens()
    {
        return $this->tokens;
    }

    /**
     * @return array
     */
    public function getClasses()
    {
        if ($this->classes !== null) {
            return $this->classes;
        }

        $this->parse();

        return $this->classes;
    }

    /**
     * @return array
     */
    public function getFunctions()
    {
        if ($this->functions !== null) {
            return $this->functions;
        }

        $this->parse();

        return $this->functions;
    }

    /**
     * @return array
     */
    public function getInterfaces()
    {
        if ($this->interfaces !== null) {
            return $this->interfaces;
        }

        $this->parse();

        return $this->interfaces;
    }

    /**
     * @return array
     * @since  Method available since Release 1.1.0
     */
    public function getTraits()
    {
        if ($this->traits !== null) {
            return $this->traits;
        }

        $this->parse();

        return $this->traits;
    }

    /**
     * Gets the names of all files that have been included
     * using include(), include_once(), require() or require_once().
     *
     * Parameter $categorize set to TRUE causing this function to return a
     * multi-dimensional array with categories in the keys of the first dimension
     * and constants and their values in the second dimension.
     *
     * Parameter $category allow to filter following specific inclusion type
     *
     * @param bool   $categorize OPTIONAL
     * @param string $category   OPTIONAL Either 'require_once', 'require',
     *                                           'include_once', 'include'.
     * @return array
     * @since  Method available since Release 1.1.0
     */
    public function getIncludes($categorize = false, $category = null)
    {
        if ($this->includes === null) {
            $this->includes = array(
              'require_once' => array(),
              'require'      => array(),
              'include_once' => array(),
              'include'      => array()
            );

            foreach ($this->tokens as $token) {
                switch (get_class($token)) {
                    case 'PHP_Token_REQUIRE_ONCE':
                    case 'PHP_Token_REQUIRE':
                    case 'PHP_Token_INCLUDE_ONCE':
                    case 'PHP_Token_INCLUDE':
                        $this->includes[$token->getType()][] = $token->getName();
                        break;
                }
            }
        }

        if (isset($this->includes[$category])) {
            $includes = $this->includes[$category];
        } elseif ($categorize === false) {
            $includes = array_merge(
                $this->includes['require_once'],
                $this->includes['require'],
                $this->includes['include_once'],
                $this->includes['include']
            );
        } else {
            $includes = $this->includes;
        }

        return $includes;
    }

    /**
     * Returns the name of the function or method a line belongs to.
     *
     * @return string or null if the line is not in a function or method
     * @since  Method available since Release 1.2.0
     */
    public function getFunctionForLine($line)
    {
        $this->parse();

        if (isset($this->lineToFunctionMap[$line])) {
            return $this->lineToFunctionMap[$line];
        }
    }

    protected function parse()
    {
        $this->interfaces = array();
        $this->classes    = array();
        $this->traits     = array();
        $this->functions  = array();
        $class            = array();
        $classEndLine     = array();
        $trait            = false;
        $traitEndLine     = false;
        $interface        = false;
        $interfaceEndLine = false;

        foreach ($this->tokens as $token) {
            switch (get_class($token)) {
                case 'PHP_Token_HALT_COMPILER':
                    return;

                case 'PHP_Token_INTERFACE':
                    $interface        = $token->getName();
                    $interfaceEndLine = $token->getEndLine();

                    $this->interfaces[$interface] = array(
                      'methods'   => array(),
                      'parent'    => $token->getParent(),
                      'keywords'  => $token->getKeywords(),
                      'docblock'  => $token->getDocblock(),
                      'startLine' => $token->getLine(),
                      'endLine'   => $interfaceEndLine,
                      'package'   => $token->getPackage(),
                      'file'      => $this->filename
                    );
                    break;

                case 'PHP_Token_CLASS':
                case 'PHP_Token_TRAIT':
                    $tmp = array(
                      'methods'   => array(),
                      'parent'    => $token->getParent(),
                      'interfaces'=> $token->getInterfaces(),
                      'keywords'  => $token->getKeywords(),
                      'docblock'  => $token->getDocblock(),
                      'startLine' => $token->getLine(),
                      'endLine'   => $token->getEndLine(),
                      'package'   => $token->getPackage(),
                      'file'      => $this->filename
                    );

                    if ($token instanceof PHP_Token_CLASS) {
                        $class[]        = $token->getName();
                        $classEndLine[] = $token->getEndLine();

                        if ($class[count($class)-1] != 'anonymous class') {
                            $this->classes[$class[count($class)-1]] = $tmp;
                        }
                    } else {
                        $trait                = $token->getName();
                        $traitEndLine         = $token->getEndLine();
                        $this->traits[$trait] = $tmp;
                    }
                    break;

                case 'PHP_Token_FUNCTION':
                    $name = $token->getName();
                    $tmp  = array(
                      'docblock'  => $token->getDocblock(),
                      'keywords'  => $token->getKeywords(),
                      'visibility'=> $token->getVisibility(),
                      'signature' => $token->getSignature(),
                      'startLine' => $token->getLine(),
                      'endLine'   => $token->getEndLine(),
                      'ccn'       => $token->getCCN(),
                      'file'      => $this->filename
                    );

                    if (empty($class) &&
                        $trait === false &&
                        $interface === false) {
                        $this->functions[$name] = $tmp;

                        $this->addFunctionToMap(
                            $name,
                            $tmp['startLine'],
                            $tmp['endLine']
                        );
                    } elseif (!empty($class) && $class[count($class)-1] != 'anonymous class') {
                        $this->classes[$class[count($class)-1]]['methods'][$name] = $tmp;

                        $this->addFunctionToMap(
                            $class[count($class)-1] . '::' . $name,
                            $tmp['startLine'],
                            $tmp['endLine']
                        );
                    } elseif ($trait !== false) {
                        $this->traits[$trait]['methods'][$name] = $tmp;

                        $this->addFunctionToMap(
                            $trait . '::' . $name,
                            $tmp['startLine'],
                            $tmp['endLine']
                        );
                    } else {
                        $this->interfaces[$interface]['methods'][$name] = $tmp;
                    }
                    break;

                case 'PHP_Token_CLOSE_CURLY':
                    if (!empty($classEndLine) &&
                        $classEndLine[count($classEndLine)-1] == $token->getLine()) {
                        array_pop($classEndLine);
                        array_pop($class);
                    } elseif ($traitEndLine !== false &&
                        $traitEndLine == $token->getLine()) {
                        $trait        = false;
                        $traitEndLine = false;
                    } elseif ($interfaceEndLine !== false &&
                        $interfaceEndLine == $token->getLine()) {
                        $interface        = false;
                        $interfaceEndLine = false;
                    }
                    break;
            }
        }
    }

    /**
     * @return array
     */
    public function getLinesOfCode()
    {
        return $this->linesOfCode;
    }

    /**
     */
    public function rewind()
    {
        $this->position = 0;
    }

    /**
     * @return boolean
     */
    public function valid()
    {
        return isset($this->tokens[$this->position]);
    }

    /**
     * @return integer
     */
    public function key()
    {
        return $this->position;
    }

    /**
     * @return PHP_Token
     */
    public function current()
    {
        return $this->tokens[$this->position];
    }

    /**
     */
    public function next()
    {
        $this->position++;
    }

    /**
     * @param  integer $offset
     * @return boolean
     */
    public function offsetExists($offset)
    {
        return isset($this->tokens[$offset]);
    }

    /**
     * @param  integer $offset
     * @return mixed
     * @throws OutOfBoundsException
     */
    public function offsetGet($offset)
    {
        if (!$this->offsetExists($offset)) {
            throw new OutOfBoundsException(
                sprintf(
                    'No token at position "%s"',
                    $offset
                )
            );
        }

        return $this->tokens[$offset];
    }

    /**
     * @param integer $offset
     * @param mixed   $value
     */
    public function offsetSet($offset, $value)
    {
        $this->tokens[$offset] = $value;
    }

    /**
     * @param  integer $offset
     * @throws OutOfBoundsException
     */
    public function offsetUnset($offset)
    {
        if (!$this->offsetExists($offset)) {
            throw new OutOfBoundsException(
                sprintf(
                    'No token at position "%s"',
                    $offset
                )
            );
        }

        unset($this->tokens[$offset]);
    }

    /**
     * Seek to an absolute position.
     *
     * @param  integer $position
     * @throws OutOfBoundsException
     */
    public function seek($position)
    {
        $this->position = $position;

        if (!$this->valid()) {
            throw new OutOfBoundsException(
                sprintf(
                    'No token at position "%s"',
                    $this->position
                )
            );
        }
    }

    /**
     * @param string  $name
     * @param integer $startLine
     * @param integer $endLine
     */
    private function addFunctionToMap($name, $startLine, $endLine)
    {
        for ($line = $startLine; $line <= $endLine; $line++) {
            $this->lineToFunctionMap[$line] = $name;
        }
    }
}
<?php
/*
 * This file is part of the PHP_TokenStream package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A PHP token.
 *
 * @author    Sebastian Bergmann <sebastian@phpunit.de>
 * @copyright Sebastian Bergmann <sebastian@phpunit.de>
 * @license   http://www.opensource.org/licenses/BSD-3-Clause  The BSD 3-Clause License
 * @link      http://github.com/sebastianbergmann/php-token-stream/tree
 * @since     Class available since Release 1.0.0
 */
abstract class PHP_Token
{
    /**
     * @var string
     */
    protected $text;

    /**
     * @var integer
     */
    protected $line;

    /**
     * @var PHP_Token_Stream
     */
    protected $tokenStream;

    /**
     * @var integer
     */
    protected $id;

    /**
     * Constructor.
     *
     * @param string           $text
     * @param integer          $line
     * @param PHP_Token_Stream $tokenStream
     * @param integer          $id
     */
    public function __construct($text, $line, PHP_Token_Stream $tokenStream, $id)
    {
        $this->text        = $text;
        $this->line        = $line;
        $this->tokenStream = $tokenStream;
        $this->id          = $id;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return $this->text;
    }

    /**
     * @return integer
     */
    public function getLine()
    {
        return $this->line;
    }
}

abstract class PHP_TokenWithScope extends PHP_Token
{
    /**
     * @var integer
     */
    protected $endTokenId;

    /**
     * Get the docblock for this token
     *
     * This method will fetch the docblock belonging to the current token. The
     * docblock must be placed on the line directly above the token to be
     * recognized.
     *
     * @return string|null Returns the docblock as a string if found
     */
    public function getDocblock()
    {
        $tokens            = $this->tokenStream->tokens();
        $currentLineNumber = $tokens[$this->id]->getLine();
        $prevLineNumber    = $currentLineNumber - 1;

        for ($i = $this->id - 1; $i; $i--) {
            if (!isset($tokens[$i])) {
                return;
            }

            if ($tokens[$i] instanceof PHP_Token_FUNCTION ||
                $tokens[$i] instanceof PHP_Token_CLASS ||
                $tokens[$i] instanceof PHP_Token_TRAIT) {
                // Some other trait, class or function, no docblock can be
                // used for the current token
                break;
            }

            $line = $tokens[$i]->getLine();

            if ($line == $currentLineNumber ||
                ($line == $prevLineNumber &&
                 $tokens[$i] instanceof PHP_Token_WHITESPACE)) {
                continue;
            }

            if ($line < $currentLineNumber &&
                !$tokens[$i] instanceof PHP_Token_DOC_COMMENT) {
                break;
            }

            return (string)$tokens[$i];
        }
    }

    /**
     * @return integer
     */
    public function getEndTokenId()
    {
        $block  = 0;
        $i      = $this->id;
        $tokens = $this->tokenStream->tokens();

        while ($this->endTokenId === null && isset($tokens[$i])) {
            if ($tokens[$i] instanceof PHP_Token_OPEN_CURLY ||
                $tokens[$i] instanceof PHP_Token_CURLY_OPEN) {
                $block++;
            } elseif ($tokens[$i] instanceof PHP_Token_CLOSE_CURLY) {
                $block--;

                if ($block === 0) {
                    $this->endTokenId = $i;
                }
            } elseif (($this instanceof PHP_Token_FUNCTION ||
                $this instanceof PHP_Token_NAMESPACE) &&
                $tokens[$i] instanceof PHP_Token_SEMICOLON) {
                if ($block === 0) {
                    $this->endTokenId = $i;
                }
            }

            $i++;
        }

        if ($this->endTokenId === null) {
            $this->endTokenId = $this->id;
        }

        return $this->endTokenId;
    }

    /**
     * @return integer
     */
    public function getEndLine()
    {
        return $this->tokenStream[$this->getEndTokenId()]->getLine();
    }
}

abstract class PHP_TokenWithScopeAndVisibility extends PHP_TokenWithScope
{
    /**
     * @return string
     */
    public function getVisibility()
    {
        $tokens = $this->tokenStream->tokens();

        for ($i = $this->id - 2; $i > $this->id - 7; $i -= 2) {
            if (isset($tokens[$i]) &&
               ($tokens[$i] instanceof PHP_Token_PRIVATE ||
                $tokens[$i] instanceof PHP_Token_PROTECTED ||
                $tokens[$i] instanceof PHP_Token_PUBLIC)) {
                return strtolower(
                    str_replace('PHP_Token_', '', get_class($tokens[$i]))
                );
            }
            if (isset($tokens[$i]) &&
              !($tokens[$i] instanceof PHP_Token_STATIC ||
                $tokens[$i] instanceof PHP_Token_FINAL ||
                $tokens[$i] instanceof PHP_Token_ABSTRACT)) {
                // no keywords; stop visibility search
                break;
            }
        }
    }

    /**
     * @return string
     */
    public function getKeywords()
    {
        $keywords = array();
        $tokens   = $this->tokenStream->tokens();

        for ($i = $this->id - 2; $i > $this->id - 7; $i -= 2) {
            if (isset($tokens[$i]) &&
               ($tokens[$i] instanceof PHP_Token_PRIVATE ||
                $tokens[$i] instanceof PHP_Token_PROTECTED ||
                $tokens[$i] instanceof PHP_Token_PUBLIC)) {
                continue;
            }

            if (isset($tokens[$i]) &&
               ($tokens[$i] instanceof PHP_Token_STATIC ||
                $tokens[$i] instanceof PHP_Token_FINAL ||
                $tokens[$i] instanceof PHP_Token_ABSTRACT)) {
                $keywords[] = strtolower(
                    str_replace('PHP_Token_', '', get_class($tokens[$i]))
                );
            }
        }

        return implode(',', $keywords);
    }
}

abstract class PHP_Token_Includes extends PHP_Token
{
    /**
     * @var string
     */
    protected $name;

    /**
     * @var string
     */
    protected $type;

    /**
     * @return string
     */
    public function getName()
    {
        if ($this->name === null) {
            $this->process();
        }

        return $this->name;
    }

    /**
     * @return string
     */
    public function getType()
    {
        if ($this->type === null) {
            $this->process();
        }

        return $this->type;
    }

    private function process()
    {
        $tokens = $this->tokenStream->tokens();

        if ($tokens[$this->id+2] instanceof PHP_Token_CONSTANT_ENCAPSED_STRING) {
            $this->name = trim($tokens[$this->id+2], "'\"");
            $this->type = strtolower(
                str_replace('PHP_Token_', '', get_class($tokens[$this->id]))
            );
        }
    }
}


class PHP_Token_FUNCTION extends PHP_TokenWithScopeAndVisibility
{
    /**
     * @var array
     */
    protected $arguments;

    /**
     * @var integer
     */
    protected $ccn;

    /**
     * @var string
     */
    protected $name;

    /**
     * @var string
     */
    protected $signature;

    /**
     * @return array
     */
    public function getArguments()
    {
        if ($this->arguments !== null) {
            return $this->arguments;
        }

        $this->arguments = array();
        $tokens          = $this->tokenStream->tokens();
        $typeDeclaration = null;

        // Search for first token inside brackets
        $i = $this->id + 2;

        while (!$tokens[$i-1] instanceof PHP_Token_OPEN_BRACKET) {
            $i++;
        }

        while (!$tokens[$i] instanceof PHP_Token_CLOSE_BRACKET) {
            if ($tokens[$i] instanceof PHP_Token_STRING) {
                $typeDeclaration = (string)$tokens[$i];
            } elseif ($tokens[$i] instanceof PHP_Token_VARIABLE) {
                $this->arguments[(string)$tokens[$i]] = $typeDeclaration;
                $typeDeclaration                      = null;
            }

            $i++;
        }

        return $this->arguments;
    }

    /**
     * @return string
     */
    public function getName()
    {
        if ($this->name !== null) {
            return $this->name;
        }

        $tokens = $this->tokenStream->tokens();

        for ($i = $this->id + 1; $i < count($tokens); $i++) {
            if ($tokens[$i] instanceof PHP_Token_STRING) {
                $this->name = (string)$tokens[$i];
                break;
            } elseif ($tokens[$i] instanceof PHP_Token_AMPERSAND &&
                     $tokens[$i+1] instanceof PHP_Token_STRING) {
                $this->name = (string)$tokens[$i+1];
                break;
            } elseif ($tokens[$i] instanceof PHP_Token_OPEN_BRACKET) {
                $this->name = 'anonymous function';
                break;
            }
        }

        if ($this->name != 'anonymous function') {
            for ($i = $this->id; $i; --$i) {
                if ($tokens[$i] instanceof PHP_Token_NAMESPACE) {
                    $this->name = $tokens[$i]->getName() . '\\' . $this->name;
                    break;
                }

                if ($tokens[$i] instanceof PHP_Token_INTERFACE) {
                    break;
                }
            }
        }

        return $this->name;
    }

    /**
     * @return integer
     */
    public function getCCN()
    {
        if ($this->ccn !== null) {
            return $this->ccn;
        }

        $this->ccn = 1;
        $end       = $this->getEndTokenId();
        $tokens    = $this->tokenStream->tokens();

        for ($i = $this->id; $i <= $end; $i++) {
            switch (get_class($tokens[$i])) {
                case 'PHP_Token_IF':
                case 'PHP_Token_ELSEIF':
                case 'PHP_Token_FOR':
                case 'PHP_Token_FOREACH':
                case 'PHP_Token_WHILE':
                case 'PHP_Token_CASE':
                case 'PHP_Token_CATCH':
                case 'PHP_Token_BOOLEAN_AND':
                case 'PHP_Token_LOGICAL_AND':
                case 'PHP_Token_BOOLEAN_OR':
                case 'PHP_Token_LOGICAL_OR':
                case 'PHP_Token_QUESTION_MARK':
                    $this->ccn++;
                    break;
            }
        }

        return $this->ccn;
    }

    /**
     * @return string
     */
    public function getSignature()
    {
        if ($this->signature !== null) {
            return $this->signature;
        }

        if ($this->getName() == 'anonymous function') {
            $this->signature = 'anonymous function';
            $i               = $this->id + 1;
        } else {
            $this->signature = '';
            $i               = $this->id + 2;
        }

        $tokens = $this->tokenStream->tokens();

        while (isset($tokens[$i]) &&
               !$tokens[$i] instanceof PHP_Token_OPEN_CURLY &&
               !$tokens[$i] instanceof PHP_Token_SEMICOLON) {
            $this->signature .= $tokens[$i++];
        }

        $this->signature = trim($this->signature);

        return $this->signature;
    }
}

class PHP_Token_INTERFACE extends PHP_TokenWithScopeAndVisibility
{
    /**
     * @var array
     */
    protected $interfaces;

    /**
     * @return string
     */
    public function getName()
    {
        return (string)$this->tokenStream[$this->id + 2];
    }

    /**
     * @return boolean
     */
    public function hasParent()
    {
        return $this->tokenStream[$this->id + 4] instanceof PHP_Token_EXTENDS;
    }

    /**
     * @return array
     */
    public function getPackage()
    {
        $className  = $this->getName();
        $docComment = $this->getDocblock();

        $result = array(
            'namespace'   => '',
            'fullPackage' => '',
            'category'    => '',
            'package'     => '',
            'subpackage'  => ''
        );

        for ($i = $this->id; $i; --$i) {
            if ($this->tokenStream[$i] instanceof PHP_Token_NAMESPACE) {
                $result['namespace'] = $this->tokenStream[$i]->getName();
                break;
            }
        }

        if (preg_match('/@category[\s]+([\.\w]+)/', $docComment, $matches)) {
            $result['category'] = $matches[1];
        }

        if (preg_match('/@package[\s]+([\.\w]+)/', $docComment, $matches)) {
            $result['package']     = $matches[1];
            $result['fullPackage'] = $matches[1];
        }

        if (preg_match('/@subpackage[\s]+([\.\w]+)/', $docComment, $matches)) {
            $result['subpackage']   = $matches[1];
            $result['fullPackage'] .= '.' . $matches[1];
        }

        if (empty($result['fullPackage'])) {
            $result['fullPackage'] = $this->arrayToName(
                explode('_', str_replace('\\', '_', $className)),
                '.'
            );
        }

        return $result;
    }

    /**
     * @param  array  $parts
     * @param  string $join
     * @return string
     */
    protected function arrayToName(array $parts, $join = '\\')
    {
        $result = '';

        if (count($parts) > 1) {
            array_pop($parts);

            $result = join($join, $parts);
        }

        return $result;
    }

    /**
     * @return boolean|string
     */
    public function getParent()
    {
        if (!$this->hasParent()) {
            return false;
        }

        $i         = $this->id + 6;
        $tokens    = $this->tokenStream->tokens();
        $className = (string)$tokens[$i];

        while (isset($tokens[$i+1]) &&
               !$tokens[$i+1] instanceof PHP_Token_WHITESPACE) {
            $className .= (string)$tokens[++$i];
        }

        return $className;
    }

    /**
     * @return boolean
     */
    public function hasInterfaces()
    {
        return (isset($this->tokenStream[$this->id + 4]) &&
                $this->tokenStream[$this->id + 4] instanceof PHP_Token_IMPLEMENTS) ||
               (isset($this->tokenStream[$this->id + 8]) &&
                $this->tokenStream[$this->id + 8] instanceof PHP_Token_IMPLEMENTS);
    }

    /**
     * @return array|boolean
     */
    public function getInterfaces()
    {
        if ($this->interfaces !== null) {
            return $this->interfaces;
        }

        if (!$this->hasInterfaces()) {
            return ($this->interfaces = false);
        }

        if ($this->tokenStream[$this->id + 4] instanceof PHP_Token_IMPLEMENTS) {
            $i = $this->id + 3;
        } else {
            $i = $this->id + 7;
        }

        $tokens = $this->tokenStream->tokens();

        while (!$tokens[$i+1] instanceof PHP_Token_OPEN_CURLY) {
            $i++;

            if ($tokens[$i] instanceof PHP_Token_STRING) {
                $this->interfaces[] = (string)$tokens[$i];
            }
        }

        return $this->interfaces;
    }
}

class PHP_Token_ABSTRACT extends PHP_Token {}
class PHP_Token_AMPERSAND extends PHP_Token {}
class PHP_Token_AND_EQUAL extends PHP_Token {}
class PHP_Token_ARRAY extends PHP_Token {}
class PHP_Token_ARRAY_CAST extends PHP_Token {}
class PHP_Token_AS extends PHP_Token {}
class PHP_Token_AT extends PHP_Token {}
class PHP_Token_BACKTICK extends PHP_Token {}
class PHP_Token_BAD_CHARACTER extends PHP_Token {}
class PHP_Token_BOOLEAN_AND extends PHP_Token {}
class PHP_Token_BOOLEAN_OR extends PHP_Token {}
class PHP_Token_BOOL_CAST extends PHP_Token {}
class PHP_Token_BREAK extends PHP_Token {}
class PHP_Token_CARET extends PHP_Token {}
class PHP_Token_CASE extends PHP_Token {}
class PHP_Token_CATCH extends PHP_Token {}
class PHP_Token_CHARACTER extends PHP_Token {}

class PHP_Token_CLASS extends PHP_Token_INTERFACE
{
    /**
     * @return string
     */
    public function getName()
    {
        $next = $this->tokenStream[$this->id + 1];

        if ($next instanceof PHP_Token_WHITESPACE) {
            $next = $this->tokenStream[$this->id + 2];
        }

        if ($next instanceof PHP_Token_STRING) {
            return (string) $next;
        }

        if ($next instanceof PHP_Token_OPEN_CURLY ||
            $next instanceof PHP_Token_EXTENDS ||
            $next instanceof PHP_Token_IMPLEMENTS) {
            return 'anonymous class';
        }
    }
}

class PHP_Token_CLASS_C extends PHP_Token {}
class PHP_Token_CLASS_NAME_CONSTANT extends PHP_Token {}
class PHP_Token_CLONE extends PHP_Token {}
class PHP_Token_CLOSE_BRACKET extends PHP_Token {}
class PHP_Token_CLOSE_CURLY extends PHP_Token {}
class PHP_Token_CLOSE_SQUARE extends PHP_Token {}
class PHP_Token_CLOSE_TAG extends PHP_Token {}
class PHP_Token_COLON extends PHP_Token {}
class PHP_Token_COMMA extends PHP_Token {}
class PHP_Token_COMMENT extends PHP_Token {}
class PHP_Token_CONCAT_EQUAL extends PHP_Token {}
class PHP_Token_CONST extends PHP_Token {}
class PHP_Token_CONSTANT_ENCAPSED_STRING extends PHP_Token {}
class PHP_Token_CONTINUE extends PHP_Token {}
class PHP_Token_CURLY_OPEN extends PHP_Token {}
class PHP_Token_DEC extends PHP_Token {}
class PHP_Token_DECLARE extends PHP_Token {}
class PHP_Token_DEFAULT extends PHP_Token {}
class PHP_Token_DIV extends PHP_Token {}
class PHP_Token_DIV_EQUAL extends PHP_Token {}
class PHP_Token_DNUMBER extends PHP_Token {}
class PHP_Token_DO extends PHP_Token {}
class PHP_Token_DOC_COMMENT extends PHP_Token {}
class PHP_Token_DOLLAR extends PHP_Token {}
class PHP_Token_DOLLAR_OPEN_CURLY_BRACES extends PHP_Token {}
class PHP_Token_DOT extends PHP_Token {}
class PHP_Token_DOUBLE_ARROW extends PHP_Token {}
class PHP_Token_DOUBLE_CAST extends PHP_Token {}
class PHP_Token_DOUBLE_COLON extends PHP_Token {}
class PHP_Token_DOUBLE_QUOTES extends PHP_Token {}
class PHP_Token_ECHO extends PHP_Token {}
class PHP_Token_ELSE extends PHP_Token {}
class PHP_Token_ELSEIF extends PHP_Token {}
class PHP_Token_EMPTY extends PHP_Token {}
class PHP_Token_ENCAPSED_AND_WHITESPACE extends PHP_Token {}
class PHP_Token_ENDDECLARE extends PHP_Token {}
class PHP_Token_ENDFOR extends PHP_Token {}
class PHP_Token_ENDFOREACH extends PHP_Token {}
class PHP_Token_ENDIF extends PHP_Token {}
class PHP_Token_ENDSWITCH extends PHP_Token {}
class PHP_Token_ENDWHILE extends PHP_Token {}
class PHP_Token_END_HEREDOC extends PHP_Token {}
class PHP_Token_EQUAL extends PHP_Token {}
class PHP_Token_EVAL extends PHP_Token {}
class PHP_Token_EXCLAMATION_MARK extends PHP_Token {}
class PHP_Token_EXIT extends PHP_Token {}
class PHP_Token_EXTENDS extends PHP_Token {}
class PHP_Token_FILE extends PHP_Token {}
class PHP_Token_FINAL extends PHP_Token {}
class PHP_Token_FOR extends PHP_Token {}
class PHP_Token_FOREACH extends PHP_Token {}
class PHP_Token_FUNC_C extends PHP_Token {}
class PHP_Token_GLOBAL extends PHP_Token {}
class PHP_Token_GT extends PHP_Token {}
class PHP_Token_IF extends PHP_Token {}
class PHP_Token_IMPLEMENTS extends PHP_Token {}
class PHP_Token_INC extends PHP_Token {}
class PHP_Token_INCLUDE extends PHP_Token_Includes {}
class PHP_Token_INCLUDE_ONCE extends PHP_Token_Includes {}
class PHP_Token_INLINE_HTML extends PHP_Token {}
class PHP_Token_INSTANCEOF extends PHP_Token {}
class PHP_Token_INT_CAST extends PHP_Token {}
class PHP_Token_ISSET extends PHP_Token {}
class PHP_Token_IS_EQUAL extends PHP_Token {}
class PHP_Token_IS_GREATER_OR_EQUAL extends PHP_Token {}
class PHP_Token_IS_IDENTICAL extends PHP_Token {}
class PHP_Token_IS_NOT_EQUAL extends PHP_Token {}
class PHP_Token_IS_NOT_IDENTICAL extends PHP_Token {}
class PHP_Token_IS_SMALLER_OR_EQUAL extends PHP_Token {}
class PHP_Token_LINE extends PHP_Token {}
class PHP_Token_LIST extends PHP_Token {}
class PHP_Token_LNUMBER extends PHP_Token {}
class PHP_Token_LOGICAL_AND extends PHP_Token {}
class PHP_Token_LOGICAL_OR extends PHP_Token {}
class PHP_Token_LOGICAL_XOR extends PHP_Token {}
class PHP_Token_LT extends PHP_Token {}
class PHP_Token_METHOD_C extends PHP_Token {}
class PHP_Token_MINUS extends PHP_Token {}
class PHP_Token_MINUS_EQUAL extends PHP_Token {}
class PHP_Token_MOD_EQUAL extends PHP_Token {}
class PHP_Token_MULT extends PHP_Token {}
class PHP_Token_MUL_EQUAL extends PHP_Token {}
class PHP_Token_NEW extends PHP_Token {}
class PHP_Token_NUM_STRING extends PHP_Token {}
class PHP_Token_OBJECT_CAST extends PHP_Token {}
class PHP_Token_OBJECT_OPERATOR extends PHP_Token {}
class PHP_Token_OPEN_BRACKET extends PHP_Token {}
class PHP_Token_OPEN_CURLY extends PHP_Token {}
class PHP_Token_OPEN_SQUARE extends PHP_Token {}
class PHP_Token_OPEN_TAG extends PHP_Token {}
class PHP_Token_OPEN_TAG_WITH_ECHO extends PHP_Token {}
class PHP_Token_OR_EQUAL extends PHP_Token {}
class PHP_Token_PAAMAYIM_NEKUDOTAYIM extends PHP_Token {}
class PHP_Token_PERCENT extends PHP_Token {}
class PHP_Token_PIPE extends PHP_Token {}
class PHP_Token_PLUS extends PHP_Token {}
class PHP_Token_PLUS_EQUAL extends PHP_Token {}
class PHP_Token_PRINT extends PHP_Token {}
class PHP_Token_PRIVATE extends PHP_Token {}
class PHP_Token_PROTECTED extends PHP_Token {}
class PHP_Token_PUBLIC extends PHP_Token {}
class PHP_Token_QUESTION_MARK extends PHP_Token {}
class PHP_Token_REQUIRE extends PHP_Token_Includes {}
class PHP_Token_REQUIRE_ONCE extends PHP_Token_Includes {}
class PHP_Token_RETURN extends PHP_Token {}
class PHP_Token_SEMICOLON extends PHP_Token {}
class PHP_Token_SL extends PHP_Token {}
class PHP_Token_SL_EQUAL extends PHP_Token {}
class PHP_Token_SR extends PHP_Token {}
class PHP_Token_SR_EQUAL extends PHP_Token {}
class PHP_Token_START_HEREDOC extends PHP_Token {}
class PHP_Token_STATIC extends PHP_Token {}
class PHP_Token_STRING extends PHP_Token {}
class PHP_Token_STRING_CAST extends PHP_Token {}
class PHP_Token_STRING_VARNAME extends PHP_Token {}
class PHP_Token_SWITCH extends PHP_Token {}
class PHP_Token_THROW extends PHP_Token {}
class PHP_Token_TILDE extends PHP_Token {}
class PHP_Token_TRY extends PHP_Token {}
class PHP_Token_UNSET extends PHP_Token {}
class PHP_Token_UNSET_CAST extends PHP_Token {}
class PHP_Token_USE extends PHP_Token {}
class PHP_Token_USE_FUNCTION extends PHP_Token {}
class PHP_Token_VAR extends PHP_Token {}
class PHP_Token_VARIABLE extends PHP_Token {}
class PHP_Token_WHILE extends PHP_Token {}
class PHP_Token_WHITESPACE extends PHP_Token {}
class PHP_Token_XOR_EQUAL extends PHP_Token {}

// Tokens introduced in PHP 5.1
class PHP_Token_HALT_COMPILER extends PHP_Token {}

// Tokens introduced in PHP 5.3
class PHP_Token_DIR extends PHP_Token {}
class PHP_Token_GOTO extends PHP_Token {}

class PHP_Token_NAMESPACE extends PHP_TokenWithScope
{
    /**
     * @return string
     */
    public function getName()
    {
        $tokens    = $this->tokenStream->tokens();
        $namespace = (string)$tokens[$this->id+2];

        for ($i = $this->id + 3;; $i += 2) {
            if (isset($tokens[$i]) &&
                $tokens[$i] instanceof PHP_Token_NS_SEPARATOR) {
                $namespace .= '\\' . $tokens[$i+1];
            } else {
                break;
            }
        }

        return $namespace;
    }
}

class PHP_Token_NS_C extends PHP_Token {}
class PHP_Token_NS_SEPARATOR extends PHP_Token {}

// Tokens introduced in PHP 5.4
class PHP_Token_CALLABLE extends PHP_Token {}
class PHP_Token_INSTEADOF extends PHP_Token {}
class PHP_Token_TRAIT extends PHP_Token_INTERFACE {}
class PHP_Token_TRAIT_C extends PHP_Token {}

// Tokens introduced in PHP 5.5
class PHP_Token_FINALLY extends PHP_Token {}
class PHP_Token_YIELD extends PHP_Token {}

// Tokens introduced in PHP 5.6
class PHP_Token_ELLIPSIS extends PHP_Token {}
class PHP_Token_POW extends PHP_Token {}
class PHP_Token_POW_EQUAL extends PHP_Token {}

// Tokens introduced in PHP 7.0
class PHP_Token_COALESCE extends PHP_Token {}
class PHP_Token_SPACESHIP extends PHP_Token {}
class PHP_Token_YIELD_FROM extends PHP_Token {}

// Tokens introduced in HackLang / HHVM
class PHP_Token_ASYNC extends PHP_Token {}
class PHP_Token_AWAIT extends PHP_Token {}
class PHP_Token_COMPILER_HALT_OFFSET extends PHP_Token {}
class PHP_Token_ENUM extends PHP_Token {}
class PHP_Token_EQUALS extends PHP_Token {}
class PHP_Token_IN extends PHP_Token {}
class PHP_Token_JOIN extends PHP_Token {}
class PHP_Token_LAMBDA_ARROW extends PHP_Token {}
class PHP_Token_LAMBDA_CP extends PHP_Token {}
class PHP_Token_LAMBDA_OP extends PHP_Token {}
class PHP_Token_ONUMBER extends PHP_Token {}
class PHP_Token_NULLSAFE_OBJECT_OPERATOR extends PHP_Token {}
class PHP_Token_SHAPE extends PHP_Token {}
class PHP_Token_SUPER extends PHP_Token {}
class PHP_Token_TYPE extends PHP_Token {}
class PHP_Token_TYPELIST_GT extends PHP_Token {}
class PHP_Token_TYPELIST_LT extends PHP_Token {}
class PHP_Token_WHERE extends PHP_Token {}
class PHP_Token_XHP_ATTRIBUTE extends PHP_Token {}
class PHP_Token_XHP_CATEGORY extends PHP_Token {}
class PHP_Token_XHP_CATEGORY_LABEL extends PHP_Token {}
class PHP_Token_XHP_CHILDREN extends PHP_Token {}
class PHP_Token_XHP_LABEL extends PHP_Token {}
class PHP_Token_XHP_REQUIRED extends PHP_Token {}
class PHP_Token_XHP_TAG_GT extends PHP_Token {}
class PHP_Token_XHP_TAG_LT extends PHP_Token {}
class PHP_Token_XHP_TEXT extends PHP_Token {}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Marker interface for PHPUnit exceptions.
 */
interface PHPUnit_Exception
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * We have a TestSuite object A.
 * In TestSuite object A we have Tests tagged with @group.
 * We want a TestSuite object B that contains TestSuite objects C, D, ...
 * for the Tests tagged with @group C, @group D, ...
 * Running the Tests from TestSuite object B results in Tests tagged with both
 *
 * @group C and @group D in TestSuite object A to be run twice .
 *
 * <code>
 * $suite = new PHPUnit_Extensions_GroupTestSuite($A, array('C', 'D'));
 * </code>
 */
class PHPUnit_Extensions_GroupTestSuite extends PHPUnit_Framework_TestSuite
{
    public function __construct(PHPUnit_Framework_TestSuite $suite, array $groups)
    {
        $groupSuites = [];
        $name        = $suite->getName();

        foreach ($groups as $group) {
            $groupSuites[$group] = new PHPUnit_Framework_TestSuite($name . ' - ' . $group);
            $this->addTest($groupSuites[$group]);
        }

        $tests = new RecursiveIteratorIterator(
            new PHPUnit_Util_TestSuiteIterator($suite),
            RecursiveIteratorIterator::LEAVES_ONLY
        );

        foreach ($tests as $test) {
            if ($test instanceof PHPUnit_Framework_TestCase) {
                $testGroups = PHPUnit_Util_Test::getGroups(
                    get_class($test),
                    $test->getName(false)
                );

                foreach ($groups as $group) {
                    foreach ($testGroups as $testGroup) {
                        if ($group == $testGroup) {
                            $groupSuites[$group]->addTest($test);
                        }
                    }
                }
            }
        }
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Runner for PHPT test cases.
 */
class PHPUnit_Extensions_PhptTestCase implements PHPUnit_Framework_Test, PHPUnit_Framework_SelfDescribing
{
    /**
     * @var string
     */
    private $filename;

    /**
     * @var PHPUnit_Util_PHP
     */
    private $phpUtil;

    /**
     * @var array
     */
    private $settings = [
        'allow_url_fopen=1',
        'auto_append_file=',
        'auto_prepend_file=',
        'disable_functions=',
        'display_errors=1',
        'docref_root=',
        'docref_ext=.html',
        'error_append_string=',
        'error_prepend_string=',
        'error_reporting=-1',
        'html_errors=0',
        'log_errors=0',
        'magic_quotes_runtime=0',
        'output_handler=',
        'open_basedir=',
        'output_buffering=Off',
        'report_memleaks=0',
        'report_zend_debug=0',
        'safe_mode=0',
        'xdebug.default_enable=0'
    ];

    /**
     * Constructs a test case with the given filename.
     *
     * @param string           $filename
     * @param PHPUnit_Util_PHP $phpUtil
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function __construct($filename, $phpUtil = null)
    {
        if (!is_string($filename)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!is_file($filename)) {
            throw new PHPUnit_Framework_Exception(
                sprintf(
                    'File "%s" does not exist.',
                    $filename
                )
            );
        }

        $this->filename = $filename;
        $this->phpUtil  = $phpUtil ?: PHPUnit_Util_PHP::factory();
    }

    /**
     * Counts the number of test cases executed by run(TestResult result).
     *
     * @return int
     */
    public function count()
    {
        return 1;
    }

    /**
     * @param array  $sections
     * @param string $output
     */
    private function assertPhptExpectation(array $sections, $output)
    {
        $assertions = [
            'EXPECT'      => 'assertEquals',
            'EXPECTF'     => 'assertStringMatchesFormat',
            'EXPECTREGEX' => 'assertRegExp',
        ];

        $actual = preg_replace('/\r\n/', "\n", trim($output));

        foreach ($assertions as $sectionName => $sectionAssertion) {
            if (isset($sections[$sectionName])) {
                $sectionContent = preg_replace('/\r\n/', "\n", trim($sections[$sectionName]));
                $assertion      = $sectionAssertion;
                $expected       = $sectionName == 'EXPECTREGEX' ? "/{$sectionContent}/" : $sectionContent;

                break;
            }
        }

        PHPUnit_Framework_Assert::$assertion($expected, $actual);
    }

    /**
     * Runs a test and collects its result in a TestResult instance.
     *
     * @param PHPUnit_Framework_TestResult $result
     *
     * @return PHPUnit_Framework_TestResult
     */
    public function run(PHPUnit_Framework_TestResult $result = null)
    {
        $sections = $this->parse();
        $code     = $this->render($sections['FILE']);

        if ($result === null) {
            $result = new PHPUnit_Framework_TestResult;
        }

        $skip     = false;
        $xfail    = false;
        $time     = 0;
        $settings = $this->settings;

        $result->startTest($this);

        if (isset($sections['INI'])) {
            $settings = array_merge($settings, $this->parseIniSection($sections['INI']));
        }

        if (isset($sections['ENV'])) {
            $env = $this->parseEnvSection($sections['ENV']);
            $this->phpUtil->setEnv($env);
        }

        // Redirects STDERR to STDOUT
        $this->phpUtil->setUseStderrRedirection(true);

        if ($result->enforcesTimeLimit()) {
            $this->phpUtil->setTimeout($result->getTimeoutForLargeTests());
        }

        if (isset($sections['SKIPIF'])) {
            $jobResult = $this->phpUtil->runJob($sections['SKIPIF'], $settings);

            if (!strncasecmp('skip', ltrim($jobResult['stdout']), 4)) {
                if (preg_match('/^\s*skip\s*(.+)\s*/i', $jobResult['stdout'], $message)) {
                    $message = substr($message[1], 2);
                } else {
                    $message = '';
                }

                $result->addFailure($this, new PHPUnit_Framework_SkippedTestError($message), 0);

                $skip = true;
            }
        }

        if (isset($sections['XFAIL'])) {
            $xfail = trim($sections['XFAIL']);
        }

        if (!$skip) {
            if (isset($sections['STDIN'])) {
                $this->phpUtil->setStdin($sections['STDIN']);
            }

            if (isset($sections['ARGS'])) {
                $this->phpUtil->setArgs($sections['ARGS']);
            }

            PHP_Timer::start();

            $jobResult = $this->phpUtil->runJob($code, $settings);
            $time      = PHP_Timer::stop();

            try {
                $this->assertPhptExpectation($sections, $jobResult['stdout']);
            } catch (PHPUnit_Framework_AssertionFailedError $e) {
                if ($xfail !== false) {
                    $result->addFailure(
                        $this,
                        new PHPUnit_Framework_IncompleteTestError(
                            $xfail,
                            0,
                            $e
                        ),
                        $time
                    );
                } else {
                    $result->addFailure($this, $e, $time);
                }
            } catch (Throwable $t) {
                $result->addError($this, $t, $time);
            } catch (Exception $e) {
                $result->addError($this, $e, $time);
            }

            if ($result->allCompletelyImplemented() && $xfail !== false) {
                $result->addFailure(
                    $this,
                    new PHPUnit_Framework_IncompleteTestError(
                        'XFAIL section but test passes'
                    ),
                    $time
                );
            }

            $this->phpUtil->setStdin('');
            $this->phpUtil->setArgs('');

            if (isset($sections['CLEAN'])) {
                $cleanCode = $this->render($sections['CLEAN']);

                $this->phpUtil->runJob($cleanCode, $this->settings);
            }
        }

        $result->endTest($this, $time);

        return $result;
    }

    /**
     * Returns the name of the test case.
     *
     * @return string
     */
    public function getName()
    {
        return $this->toString();
    }

    /**
     * Returns a string representation of the test case.
     *
     * @return string
     */
    public function toString()
    {
        return $this->filename;
    }

    /**
     * @return array
     *
     * @throws PHPUnit_Framework_Exception
     */
    private function parse()
    {
        $sections = [];
        $section  = '';

        $allowExternalSections = [
            'FILE',
            'EXPECT',
            'EXPECTF',
            'EXPECTREGEX'
        ];

        $requiredSections = [
            'FILE',
            [
                'EXPECT',
                'EXPECTF',
                'EXPECTREGEX'
            ]
        ];

        $unsupportedSections = [
            'REDIRECTTEST',
            'REQUEST',
            'POST',
            'PUT',
            'POST_RAW',
            'GZIP_POST',
            'DEFLATE_POST',
            'GET',
            'COOKIE',
            'HEADERS',
            'CGI',
            'EXPECTHEADERS',
            'EXTENSIONS',
            'PHPDBG'
        ];

        foreach (file($this->filename) as $line) {
            if (preg_match('/^--([_A-Z]+)--/', $line, $result)) {
                $section            = $result[1];
                $sections[$section] = '';

                continue;
            } elseif (empty($section)) {
                throw new PHPUnit_Framework_Exception('Invalid PHPT file');
            }

            $sections[$section] .= $line;
        }

        if (isset($sections['FILEEOF'])) {
            $sections['FILE'] = rtrim($sections['FILEEOF'], "\r\n");
            unset($sections['FILEEOF']);
        }

        $testDirectory = dirname($this->filename) . DIRECTORY_SEPARATOR;

        foreach ($allowExternalSections as $section) {
            if (isset($sections[$section . '_EXTERNAL'])) {
                // do not allow directory traversal
                $externalFilename = str_replace('..', '', trim($sections[$section . '_EXTERNAL']));

                // only allow files from the test directory
                if (!is_file($testDirectory . $externalFilename) || !is_readable($testDirectory . $externalFilename)) {
                    throw new PHPUnit_Framework_Exception(
                        sprintf(
                            'Could not load --%s-- %s for PHPT file',
                            $section . '_EXTERNAL',
                            $testDirectory . $externalFilename
                        )
                    );
                }

                $sections[$section] = file_get_contents($testDirectory . $externalFilename);

                unset($sections[$section . '_EXTERNAL']);
            }
        }

        $isValid = true;

        foreach ($requiredSections as $section) {
            if (is_array($section)) {
                $foundSection = false;

                foreach ($section as $anySection) {
                    if (isset($sections[$anySection])) {
                        $foundSection = true;

                        break;
                    }
                }

                if (!$foundSection) {
                    $isValid = false;

                    break;
                }
            } else {
                if (!isset($sections[$section])) {
                    $isValid = false;

                    break;
                }
            }
        }

        if (!$isValid) {
            throw new PHPUnit_Framework_Exception('Invalid PHPT file');
        }

        foreach ($unsupportedSections as $section) {
            if (isset($sections[$section])) {
                throw new PHPUnit_Framework_Exception(
                    'PHPUnit does not support this PHPT file'
                );
            }
        }

        return $sections;
    }

    /**
     * @param string $code
     *
     * @return string
     */
    private function render($code)
    {
        return str_replace(
            [
                '__DIR__',
                '__FILE__'
            ],
            [
                "'" . dirname($this->filename) . "'",
                "'" . $this->filename . "'"
            ],
            $code
        );
    }

    /**
     * Parse --INI-- section key value pairs and return as array.
     *
     * @param string
     *
     * @return array
     */
    protected function parseIniSection($content)
    {
        return preg_split('/\n|\r/', $content, -1, PREG_SPLIT_NO_EMPTY);
    }

    protected function parseEnvSection($content)
    {
        $env = [];

        foreach (explode("\n", trim($content)) as $e) {
            $e = explode('=', trim($e), 2);

            if (!empty($e[0]) && isset($e[1])) {
                $env[$e[0]] = $e[1];
            }
        }

        return $env;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Suite for .phpt test cases.
 */
class PHPUnit_Extensions_PhptTestSuite extends PHPUnit_Framework_TestSuite
{
    /**
     * Constructs a new TestSuite for .phpt test cases.
     *
     * @param string $directory
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function __construct($directory)
    {
        if (is_string($directory) && is_dir($directory)) {
            $this->setName($directory);

            $facade = new File_Iterator_Facade;
            $files  = $facade->getFilesAsArray($directory, '.phpt');

            foreach ($files as $file) {
                $this->addTestFile($file);
            }
        } else {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'directory name');
        }
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A Decorator that runs a test repeatedly.
 */
class PHPUnit_Extensions_RepeatedTest extends PHPUnit_Extensions_TestDecorator
{
    /**
     * @var bool
     */
    protected $processIsolation = false;

    /**
     * @var int
     */
    protected $timesRepeat = 1;

    /**
     * @param PHPUnit_Framework_Test $test
     * @param int                    $timesRepeat
     * @param bool                   $processIsolation
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function __construct(PHPUnit_Framework_Test $test, $timesRepeat = 1, $processIsolation = false)
    {
        parent::__construct($test);

        if (is_int($timesRepeat) &&
            $timesRepeat >= 0) {
            $this->timesRepeat = $timesRepeat;
        } else {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(
                2,
                'positive integer'
            );
        }

        $this->processIsolation = $processIsolation;
    }

    /**
     * Counts the number of test cases that
     * will be run by this test.
     *
     * @return int
     */
    public function count()
    {
        return $this->timesRepeat * count($this->test);
    }

    /**
     * Runs the decorated test and collects the
     * result in a TestResult.
     *
     * @param PHPUnit_Framework_TestResult $result
     *
     * @return PHPUnit_Framework_TestResult
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function run(PHPUnit_Framework_TestResult $result = null)
    {
        if ($result === null) {
            $result = $this->createResult();
        }

        //@codingStandardsIgnoreStart
        for ($i = 0; $i < $this->timesRepeat && !$result->shouldStop(); $i++) {
            //@codingStandardsIgnoreEnd
            if ($this->test instanceof PHPUnit_Framework_TestSuite) {
                $this->test->setRunTestInSeparateProcess($this->processIsolation);
            }
            $this->test->run($result);
        }

        return $result;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A Decorator for Tests.
 *
 * Use TestDecorator as the base class for defining new
 * test decorators. Test decorator subclasses can be introduced
 * to add behaviour before or after a test is run.
 */
class PHPUnit_Extensions_TestDecorator extends PHPUnit_Framework_Assert implements PHPUnit_Framework_Test, PHPUnit_Framework_SelfDescribing
{
    /**
     * The Test to be decorated.
     *
     * @var object
     */
    protected $test = null;

    /**
     * Constructor.
     *
     * @param PHPUnit_Framework_Test $test
     */
    public function __construct(PHPUnit_Framework_Test $test)
    {
        $this->test = $test;
    }

    /**
     * Returns a string representation of the test.
     *
     * @return string
     */
    public function toString()
    {
        return $this->test->toString();
    }

    /**
     * Runs the test and collects the
     * result in a TestResult.
     *
     * @param PHPUnit_Framework_TestResult $result
     */
    public function basicRun(PHPUnit_Framework_TestResult $result)
    {
        $this->test->run($result);
    }

    /**
     * Counts the number of test cases that
     * will be run by this test.
     *
     * @return int
     */
    public function count()
    {
        return count($this->test);
    }

    /**
     * Creates a default TestResult object.
     *
     * @return PHPUnit_Framework_TestResult
     */
    protected function createResult()
    {
        return new PHPUnit_Framework_TestResult;
    }

    /**
     * Returns the test to be run.
     *
     * @return PHPUnit_Framework_Test
     */
    public function getTest()
    {
        return $this->test;
    }

    /**
     * Runs the decorated test and collects the
     * result in a TestResult.
     *
     * @param PHPUnit_Framework_TestResult $result
     *
     * @return PHPUnit_Framework_TestResult
     */
    public function run(PHPUnit_Framework_TestResult $result = null)
    {
        if ($result === null) {
            $result = $this->createResult();
        }

        $this->basicRun($result);

        return $result;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Base class for test listeners that interact with an issue tracker.
 */
abstract class PHPUnit_Extensions_TicketListener implements PHPUnit_Framework_TestListener
{
    /**
     * @var array
     */
    protected $ticketCounts = [];

    /**
     * @var bool
     */
    protected $ran = false;

    /**
     * An error occurred.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
    }

    /**
     * A failure occurred.
     *
     * @param PHPUnit_Framework_Test                 $test
     * @param PHPUnit_Framework_AssertionFailedError $e
     * @param float                                  $time
     */
    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
    {
    }

    /**
     * Incomplete test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
    }

    /**
     * Risky test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
    }

    /**
     * Skipped test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
    }

    /**
     * A test suite started.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
    }

    /**
     * A test suite ended.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
    }

    /**
     * A test started.
     *
     * @param PHPUnit_Framework_Test $test
     */
    public function startTest(PHPUnit_Framework_Test $test)
    {
        if (!$test instanceof PHPUnit_Framework_WarningTestCase) {
            if ($this->ran) {
                return;
            }

            $name    = $test->getName(false);
            $tickets = PHPUnit_Util_Test::getTickets(get_class($test), $name);

            foreach ($tickets as $ticket) {
                $this->ticketCounts[$ticket][$name] = 1;
            }

            $this->ran = true;
        }
    }

    /**
     * A test ended.
     *
     * @param PHPUnit_Framework_Test $test
     * @param float                  $time
     */
    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
        if (!$test instanceof PHPUnit_Framework_WarningTestCase) {
            if ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) {
                $ifStatus   = ['assigned', 'new', 'reopened'];
                $newStatus  = 'closed';
                $message    = 'Automatically closed by PHPUnit (test passed).';
                $resolution = 'fixed';
                $cumulative = true;
            } elseif ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE) {
                $ifStatus   = ['closed'];
                $newStatus  = 'reopened';
                $message    = 'Automatically reopened by PHPUnit (test failed).';
                $resolution = '';
                $cumulative = false;
            } else {
                return;
            }

            $name    = $test->getName(false);
            $tickets = PHPUnit_Util_Test::getTickets(get_class($test), $name);

            foreach ($tickets as $ticket) {
                // Remove this test from the totals (if it passed).
                if ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) {
                    unset($this->ticketCounts[$ticket][$name]);
                }

                // Only close tickets if ALL referenced cases pass
                // but reopen tickets if a single test fails.
                if ($cumulative) {
                    // Determine number of to-pass tests:
                    if (count($this->ticketCounts[$ticket]) > 0) {
                        // There exist remaining test cases with this reference.
                        $adjustTicket = false;
                    } else {
                        // No remaining tickets, go ahead and adjust.
                        $adjustTicket = true;
                    }
                } else {
                    $adjustTicket = true;
                }

                $ticketInfo = $this->getTicketInfo($ticket);

                if ($adjustTicket && in_array($ticketInfo['status'], $ifStatus)) {
                    $this->updateTicket($ticket, $newStatus, $message, $resolution);
                }
            }
        }
    }

    /**
     * @param mixed $ticketId
     *
     * @return mixed
     */
    abstract protected function getTicketInfo($ticketId = null);

    /**
     * @param string $ticketId
     * @param string $newStatus
     * @param string $message
     * @param string $resolution
     */
    abstract protected function updateTicket($ticketId, $newStatus, $message, $resolution);
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace PHPUnit\Framework;

use PHPUnit_Framework_Assert;

abstract class Assert extends PHPUnit_Framework_Assert
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace PHPUnit\Framework;

use PHPUnit_Framework_AssertionFailedError;

class AssertionFailedError extends PHPUnit_Framework_AssertionFailedError
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace PHPUnit\Framework;

use PHPUnit_Framework_BaseTestListener;

abstract class BaseTestListener extends PHPUnit_Framework_BaseTestListener
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace PHPUnit\Framework;

use PHPUnit_Framework_Test;

interface Test extends PHPUnit_Framework_Test
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace PHPUnit\Framework;

use PHPUnit_Framework_TestCase;

abstract class TestCase extends PHPUnit_Framework_TestCase
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace PHPUnit\Framework;

use PHPUnit_Framework_TestListener;

interface TestListener extends PHPUnit_Framework_TestListener
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace PHPUnit\Framework;

use PHPUnit_Framework_TestSuite;

class TestSuite extends PHPUnit_Framework_TestSuite
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Returns a matcher that matches when the method is executed
 * zero or more times.
 *
 * @return PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount
 */
function any()
{
    return call_user_func_array(
        'PHPUnit_Framework_TestCase::any',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_IsAnything matcher object.
 *
 * @return PHPUnit_Framework_Constraint_IsAnything
 */
function anything()
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::anything',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_ArrayHasKey matcher object.
 *
 * @param mixed $key
 *
 * @return PHPUnit_Framework_Constraint_ArrayHasKey
 */
function arrayHasKey($key)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::arrayHasKey',
        func_get_args()
    );
}

/**
 * Asserts that an array has a specified key.
 *
 * @param mixed             $key
 * @param array|ArrayAccess $array
 * @param string            $message
 */
function assertArrayHasKey($key, $array, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertArrayHasKey',
        func_get_args()
    );
}

/**
 * Asserts that an array has a specified subset.
 *
 * @param array|ArrayAccess $subset
 * @param array|ArrayAccess $array
 * @param bool              $strict  Check for object identity
 * @param string            $message
 */
function assertArraySubset($subset, $array, $strict = false, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertArraySubset',
        func_get_args()
    );
}

/**
 * Asserts that an array does not have a specified key.
 *
 * @param mixed             $key
 * @param array|ArrayAccess $array
 * @param string            $message
 */
function assertArrayNotHasKey($key, $array, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertArrayNotHasKey',
        func_get_args()
    );
}

/**
 * Asserts that a haystack that is stored in a static attribute of a class
 * or an attribute of an object contains a needle.
 *
 * @param mixed  $needle
 * @param string $haystackAttributeName
 * @param mixed  $haystackClassOrObject
 * @param string $message
 * @param bool   $ignoreCase
 * @param bool   $checkForObjectIdentity
 * @param bool   $checkForNonObjectIdentity
 */
function assertAttributeContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeContains',
        func_get_args()
    );
}

/**
 * Asserts that a haystack that is stored in a static attribute of a class
 * or an attribute of an object contains only values of a given type.
 *
 * @param string $type
 * @param string $haystackAttributeName
 * @param mixed  $haystackClassOrObject
 * @param bool   $isNativeType
 * @param string $message
 */
function assertAttributeContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = null, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeContainsOnly',
        func_get_args()
    );
}

/**
 * Asserts the number of elements of an array, Countable or Traversable
 * that is stored in an attribute.
 *
 * @param int    $expectedCount
 * @param string $haystackAttributeName
 * @param mixed  $haystackClassOrObject
 * @param string $message
 */
function assertAttributeCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeCount',
        func_get_args()
    );
}

/**
 * Asserts that a static attribute of a class or an attribute of an object
 * is empty.
 *
 * @param string $haystackAttributeName
 * @param mixed  $haystackClassOrObject
 * @param string $message
 */
function assertAttributeEmpty($haystackAttributeName, $haystackClassOrObject, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeEmpty',
        func_get_args()
    );
}

/**
 * Asserts that a variable is equal to an attribute of an object.
 *
 * @param mixed  $expected
 * @param string $actualAttributeName
 * @param string $actualClassOrObject
 * @param string $message
 * @param float  $delta
 * @param int    $maxDepth
 * @param bool   $canonicalize
 * @param bool   $ignoreCase
 */
function assertAttributeEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeEquals',
        func_get_args()
    );
}

/**
 * Asserts that an attribute is greater than another value.
 *
 * @param mixed  $expected
 * @param string $actualAttributeName
 * @param string $actualClassOrObject
 * @param string $message
 */
function assertAttributeGreaterThan($expected, $actualAttributeName, $actualClassOrObject, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeGreaterThan',
        func_get_args()
    );
}

/**
 * Asserts that an attribute is greater than or equal to another value.
 *
 * @param mixed  $expected
 * @param string $actualAttributeName
 * @param string $actualClassOrObject
 * @param string $message
 */
function assertAttributeGreaterThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeGreaterThanOrEqual',
        func_get_args()
    );
}

/**
 * Asserts that an attribute is of a given type.
 *
 * @param string $expected
 * @param string $attributeName
 * @param mixed  $classOrObject
 * @param string $message
 */
function assertAttributeInstanceOf($expected, $attributeName, $classOrObject, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeInstanceOf',
        func_get_args()
    );
}

/**
 * Asserts that an attribute is of a given type.
 *
 * @param string $expected
 * @param string $attributeName
 * @param mixed  $classOrObject
 * @param string $message
 */
function assertAttributeInternalType($expected, $attributeName, $classOrObject, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeInternalType',
        func_get_args()
    );
}

/**
 * Asserts that an attribute is smaller than another value.
 *
 * @param mixed  $expected
 * @param string $actualAttributeName
 * @param string $actualClassOrObject
 * @param string $message
 */
function assertAttributeLessThan($expected, $actualAttributeName, $actualClassOrObject, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeLessThan',
        func_get_args()
    );
}

/**
 * Asserts that an attribute is smaller than or equal to another value.
 *
 * @param mixed  $expected
 * @param string $actualAttributeName
 * @param string $actualClassOrObject
 * @param string $message
 */
function assertAttributeLessThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeLessThanOrEqual',
        func_get_args()
    );
}

/**
 * Asserts that a haystack that is stored in a static attribute of a class
 * or an attribute of an object does not contain a needle.
 *
 * @param mixed  $needle
 * @param string $haystackAttributeName
 * @param mixed  $haystackClassOrObject
 * @param string $message
 * @param bool   $ignoreCase
 * @param bool   $checkForObjectIdentity
 * @param bool   $checkForNonObjectIdentity
 */
function assertAttributeNotContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeNotContains',
        func_get_args()
    );
}

/**
 * Asserts that a haystack that is stored in a static attribute of a class
 * or an attribute of an object does not contain only values of a given
 * type.
 *
 * @param string $type
 * @param string $haystackAttributeName
 * @param mixed  $haystackClassOrObject
 * @param bool   $isNativeType
 * @param string $message
 */
function assertAttributeNotContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = null, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeNotContainsOnly',
        func_get_args()
    );
}

/**
 * Asserts the number of elements of an array, Countable or Traversable
 * that is stored in an attribute.
 *
 * @param int    $expectedCount
 * @param string $haystackAttributeName
 * @param mixed  $haystackClassOrObject
 * @param string $message
 */
function assertAttributeNotCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeNotCount',
        func_get_args()
    );
}

/**
 * Asserts that a static attribute of a class or an attribute of an object
 * is not empty.
 *
 * @param string $haystackAttributeName
 * @param mixed  $haystackClassOrObject
 * @param string $message
 */
function assertAttributeNotEmpty($haystackAttributeName, $haystackClassOrObject, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeNotEmpty',
        func_get_args()
    );
}

/**
 * Asserts that a variable is not equal to an attribute of an object.
 *
 * @param mixed  $expected
 * @param string $actualAttributeName
 * @param string $actualClassOrObject
 * @param string $message
 * @param float  $delta
 * @param int    $maxDepth
 * @param bool   $canonicalize
 * @param bool   $ignoreCase
 */
function assertAttributeNotEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeNotEquals',
        func_get_args()
    );
}

/**
 * Asserts that an attribute is of a given type.
 *
 * @param string $expected
 * @param string $attributeName
 * @param mixed  $classOrObject
 * @param string $message
 */
function assertAttributeNotInstanceOf($expected, $attributeName, $classOrObject, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeNotInstanceOf',
        func_get_args()
    );
}

/**
 * Asserts that an attribute is of a given type.
 *
 * @param string $expected
 * @param string $attributeName
 * @param mixed  $classOrObject
 * @param string $message
 */
function assertAttributeNotInternalType($expected, $attributeName, $classOrObject, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeNotInternalType',
        func_get_args()
    );
}

/**
 * Asserts that a variable and an attribute of an object do not have the
 * same type and value.
 *
 * @param mixed  $expected
 * @param string $actualAttributeName
 * @param object $actualClassOrObject
 * @param string $message
 */
function assertAttributeNotSame($expected, $actualAttributeName, $actualClassOrObject, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeNotSame',
        func_get_args()
    );
}

/**
 * Asserts that a variable and an attribute of an object have the same type
 * and value.
 *
 * @param mixed  $expected
 * @param string $actualAttributeName
 * @param object $actualClassOrObject
 * @param string $message
 */
function assertAttributeSame($expected, $actualAttributeName, $actualClassOrObject, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertAttributeSame',
        func_get_args()
    );
}

/**
 * Asserts that a class has a specified attribute.
 *
 * @param string $attributeName
 * @param string $className
 * @param string $message
 */
function assertClassHasAttribute($attributeName, $className, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertClassHasAttribute',
        func_get_args()
    );
}

/**
 * Asserts that a class has a specified static attribute.
 *
 * @param string $attributeName
 * @param string $className
 * @param string $message
 */
function assertClassHasStaticAttribute($attributeName, $className, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertClassHasStaticAttribute',
        func_get_args()
    );
}

/**
 * Asserts that a class does not have a specified attribute.
 *
 * @param string $attributeName
 * @param string $className
 * @param string $message
 */
function assertClassNotHasAttribute($attributeName, $className, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertClassNotHasAttribute',
        func_get_args()
    );
}

/**
 * Asserts that a class does not have a specified static attribute.
 *
 * @param string $attributeName
 * @param string $className
 * @param string $message
 */
function assertClassNotHasStaticAttribute($attributeName, $className, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertClassNotHasStaticAttribute',
        func_get_args()
    );
}

/**
 * Asserts that a haystack contains a needle.
 *
 * @param mixed  $needle
 * @param mixed  $haystack
 * @param string $message
 * @param bool   $ignoreCase
 * @param bool   $checkForObjectIdentity
 * @param bool   $checkForNonObjectIdentity
 */
function assertContains($needle, $haystack, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertContains',
        func_get_args()
    );
}

/**
 * Asserts that a haystack contains only values of a given type.
 *
 * @param string $type
 * @param mixed  $haystack
 * @param bool   $isNativeType
 * @param string $message
 */
function assertContainsOnly($type, $haystack, $isNativeType = null, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertContainsOnly',
        func_get_args()
    );
}

/**
 * Asserts that a haystack contains only instances of a given classname
 *
 * @param string            $classname
 * @param array|Traversable $haystack
 * @param string            $message
 */
function assertContainsOnlyInstancesOf($classname, $haystack, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertContainsOnlyInstancesOf',
        func_get_args()
    );
}

/**
 * Asserts the number of elements of an array, Countable or Traversable.
 *
 * @param int    $expectedCount
 * @param mixed  $haystack
 * @param string $message
 */
function assertCount($expectedCount, $haystack, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertCount',
        func_get_args()
    );
}

/**
 * Asserts that a variable is empty.
 *
 * @param mixed  $actual
 * @param string $message
 *
 * @throws PHPUnit_Framework_AssertionFailedError
 */
function assertEmpty($actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertEmpty',
        func_get_args()
    );
}

/**
 * Asserts that a hierarchy of DOMElements matches.
 *
 * @param DOMElement $expectedElement
 * @param DOMElement $actualElement
 * @param bool       $checkAttributes
 * @param string     $message
 */
function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, $checkAttributes = false, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertEqualXMLStructure',
        func_get_args()
    );
}

/**
 * Asserts that two variables are equal.
 *
 * @param mixed  $expected
 * @param mixed  $actual
 * @param string $message
 * @param float  $delta
 * @param int    $maxDepth
 * @param bool   $canonicalize
 * @param bool   $ignoreCase
 */
function assertEquals($expected, $actual, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertEquals',
        func_get_args()
    );
}

/**
 * Asserts that a condition is not true.
 *
 * @param bool   $condition
 * @param string $message
 *
 * @throws PHPUnit_Framework_AssertionFailedError
 */
function assertNotTrue($condition, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertNotTrue',
        func_get_args()
    );
}

/**
 * Asserts that a condition is false.
 *
 * @param bool   $condition
 * @param string $message
 *
 * @throws PHPUnit_Framework_AssertionFailedError
 */
function assertFalse($condition, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertFalse',
        func_get_args()
    );
}

/**
 * Asserts that the contents of one file is equal to the contents of another
 * file.
 *
 * @param string $expected
 * @param string $actual
 * @param string $message
 * @param bool   $canonicalize
 * @param bool   $ignoreCase
 */
function assertFileEquals($expected, $actual, $message = '', $canonicalize = false, $ignoreCase = false)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertFileEquals',
        func_get_args()
    );
}

/**
 * Asserts that a file exists.
 *
 * @param string $filename
 * @param string $message
 */
function assertFileExists($filename, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertFileExists',
        func_get_args()
    );
}

/**
 * Asserts that the contents of one file is not equal to the contents of
 * another file.
 *
 * @param string $expected
 * @param string $actual
 * @param string $message
 * @param bool   $canonicalize
 * @param bool   $ignoreCase
 */
function assertFileNotEquals($expected, $actual, $message = '', $canonicalize = false, $ignoreCase = false)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertFileNotEquals',
        func_get_args()
    );
}

/**
 * Asserts that a file does not exist.
 *
 * @param string $filename
 * @param string $message
 */
function assertFileNotExists($filename, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertFileNotExists',
        func_get_args()
    );
}

/**
 * Asserts that a value is greater than another value.
 *
 * @param mixed  $expected
 * @param mixed  $actual
 * @param string $message
 */
function assertGreaterThan($expected, $actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertGreaterThan',
        func_get_args()
    );
}

/**
 * Asserts that a value is greater than or equal to another value.
 *
 * @param mixed  $expected
 * @param mixed  $actual
 * @param string $message
 */
function assertGreaterThanOrEqual($expected, $actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertGreaterThanOrEqual',
        func_get_args()
    );
}

/**
 * Asserts that a variable is of a given type.
 *
 * @param string $expected
 * @param mixed  $actual
 * @param string $message
 */
function assertInstanceOf($expected, $actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertInstanceOf',
        func_get_args()
    );
}

/**
 * Asserts that a variable is of a given type.
 *
 * @param string $expected
 * @param mixed  $actual
 * @param string $message
 */
function assertInternalType($expected, $actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertInternalType',
        func_get_args()
    );
}

/**
 * Asserts that a string is a valid JSON string.
 *
 * @param string $actualJson
 * @param string $message
 */
function assertJson($actualJson, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertJson',
        func_get_args()
    );
}

/**
 * Asserts that two JSON files are equal.
 *
 * @param string $expectedFile
 * @param string $actualFile
 * @param string $message
 */
function assertJsonFileEqualsJsonFile($expectedFile, $actualFile, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertJsonFileEqualsJsonFile',
        func_get_args()
    );
}

/**
 * Asserts that two JSON files are not equal.
 *
 * @param string $expectedFile
 * @param string $actualFile
 * @param string $message
 */
function assertJsonFileNotEqualsJsonFile($expectedFile, $actualFile, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertJsonFileNotEqualsJsonFile',
        func_get_args()
    );
}

/**
 * Asserts that the generated JSON encoded object and the content of the given file are equal.
 *
 * @param string $expectedFile
 * @param string $actualJson
 * @param string $message
 */
function assertJsonStringEqualsJsonFile($expectedFile, $actualJson, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertJsonStringEqualsJsonFile',
        func_get_args()
    );
}

/**
 * Asserts that two given JSON encoded objects or arrays are equal.
 *
 * @param string $expectedJson
 * @param string $actualJson
 * @param string $message
 */
function assertJsonStringEqualsJsonString($expectedJson, $actualJson, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertJsonStringEqualsJsonString',
        func_get_args()
    );
}

/**
 * Asserts that the generated JSON encoded object and the content of the given file are not equal.
 *
 * @param string $expectedFile
 * @param string $actualJson
 * @param string $message
 */
function assertJsonStringNotEqualsJsonFile($expectedFile, $actualJson, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertJsonStringNotEqualsJsonFile',
        func_get_args()
    );
}

/**
 * Asserts that two given JSON encoded objects or arrays are not equal.
 *
 * @param string $expectedJson
 * @param string $actualJson
 * @param string $message
 */
function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertJsonStringNotEqualsJsonString',
        func_get_args()
    );
}

/**
 * Asserts that a value is smaller than another value.
 *
 * @param mixed  $expected
 * @param mixed  $actual
 * @param string $message
 */
function assertLessThan($expected, $actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertLessThan',
        func_get_args()
    );
}

/**
 * Asserts that a value is smaller than or equal to another value.
 *
 * @param mixed  $expected
 * @param mixed  $actual
 * @param string $message
 */
function assertLessThanOrEqual($expected, $actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertLessThanOrEqual',
        func_get_args()
    );
}

/**
 * Asserts that a variable is finite.
 *
 * @param mixed  $actual
 * @param string $message
 */
function assertFinite($actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertFinite',
        func_get_args()
    );
}

/**
 * Asserts that a variable is infinite.
 *
 * @param mixed  $actual
 * @param string $message
 */
function assertInfinite($actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertInfinite',
        func_get_args()
    );
}

/**
 * Asserts that a variable is nan.
 *
 * @param mixed  $actual
 * @param string $message
 */
function assertNan($actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertNan',
        func_get_args()
    );
}

/**
 * Asserts that a haystack does not contain a needle.
 *
 * @param mixed  $needle
 * @param mixed  $haystack
 * @param string $message
 * @param bool   $ignoreCase
 * @param bool   $checkForObjectIdentity
 * @param bool   $checkForNonObjectIdentity
 */
function assertNotContains($needle, $haystack, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertNotContains',
        func_get_args()
    );
}

/**
 * Asserts that a haystack does not contain only values of a given type.
 *
 * @param string $type
 * @param mixed  $haystack
 * @param bool   $isNativeType
 * @param string $message
 */
function assertNotContainsOnly($type, $haystack, $isNativeType = null, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertNotContainsOnly',
        func_get_args()
    );
}

/**
 * Asserts the number of elements of an array, Countable or Traversable.
 *
 * @param int    $expectedCount
 * @param mixed  $haystack
 * @param string $message
 */
function assertNotCount($expectedCount, $haystack, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertNotCount',
        func_get_args()
    );
}

/**
 * Asserts that a variable is not empty.
 *
 * @param mixed  $actual
 * @param string $message
 *
 * @throws PHPUnit_Framework_AssertionFailedError
 */
function assertNotEmpty($actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertNotEmpty',
        func_get_args()
    );
}

/**
 * Asserts that two variables are not equal.
 *
 * @param mixed  $expected
 * @param mixed  $actual
 * @param string $message
 * @param float  $delta
 * @param int    $maxDepth
 * @param bool   $canonicalize
 * @param bool   $ignoreCase
 */
function assertNotEquals($expected, $actual, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertNotEquals',
        func_get_args()
    );
}

/**
 * Asserts that a variable is not of a given type.
 *
 * @param string $expected
 * @param mixed  $actual
 * @param string $message
 */
function assertNotInstanceOf($expected, $actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertNotInstanceOf',
        func_get_args()
    );
}

/**
 * Asserts that a variable is not of a given type.
 *
 * @param string $expected
 * @param mixed  $actual
 * @param string $message
 */
function assertNotInternalType($expected, $actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertNotInternalType',
        func_get_args()
    );
}

/**
 * Asserts that a condition is not false.
 *
 * @param bool   $condition
 * @param string $message
 *
 * @throws PHPUnit_Framework_AssertionFailedError
 */
function assertNotFalse($condition, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertNotFalse',
        func_get_args()
    );
}

/**
 * Asserts that a variable is not null.
 *
 * @param mixed  $actual
 * @param string $message
 */
function assertNotNull($actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertNotNull',
        func_get_args()
    );
}

/**
 * Asserts that a string does not match a given regular expression.
 *
 * @param string $pattern
 * @param string $string
 * @param string $message
 */
function assertNotRegExp($pattern, $string, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertNotRegExp',
        func_get_args()
    );
}

/**
 * Asserts that two variables do not have the same type and value.
 * Used on objects, it asserts that two variables do not reference
 * the same object.
 *
 * @param mixed  $expected
 * @param mixed  $actual
 * @param string $message
 */
function assertNotSame($expected, $actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertNotSame',
        func_get_args()
    );
}

/**
 * Assert that the size of two arrays (or `Countable` or `Traversable` objects)
 * is not the same.
 *
 * @param array|Countable|Traversable $expected
 * @param array|Countable|Traversable $actual
 * @param string                      $message
 */
function assertNotSameSize($expected, $actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertNotSameSize',
        func_get_args()
    );
}

/**
 * Asserts that a variable is null.
 *
 * @param mixed  $actual
 * @param string $message
 */
function assertNull($actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertNull',
        func_get_args()
    );
}

/**
 * Asserts that an object has a specified attribute.
 *
 * @param string $attributeName
 * @param object $object
 * @param string $message
 */
function assertObjectHasAttribute($attributeName, $object, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertObjectHasAttribute',
        func_get_args()
    );
}

/**
 * Asserts that an object does not have a specified attribute.
 *
 * @param string $attributeName
 * @param object $object
 * @param string $message
 */
function assertObjectNotHasAttribute($attributeName, $object, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertObjectNotHasAttribute',
        func_get_args()
    );
}

/**
 * Asserts that a string matches a given regular expression.
 *
 * @param string $pattern
 * @param string $string
 * @param string $message
 */
function assertRegExp($pattern, $string, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertRegExp',
        func_get_args()
    );
}

/**
 * Asserts that two variables have the same type and value.
 * Used on objects, it asserts that two variables reference
 * the same object.
 *
 * @param mixed  $expected
 * @param mixed  $actual
 * @param string $message
 */
function assertSame($expected, $actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertSame',
        func_get_args()
    );
}

/**
 * Assert that the size of two arrays (or `Countable` or `Traversable` objects)
 * is the same.
 *
 * @param array|Countable|Traversable $expected
 * @param array|Countable|Traversable $actual
 * @param string                      $message
 */
function assertSameSize($expected, $actual, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertSameSize',
        func_get_args()
    );
}

/**
 * Asserts that a string ends not with a given prefix.
 *
 * @param string $suffix
 * @param string $string
 * @param string $message
 */
function assertStringEndsNotWith($suffix, $string, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertStringEndsNotWith',
        func_get_args()
    );
}

/**
 * Asserts that a string ends with a given prefix.
 *
 * @param string $suffix
 * @param string $string
 * @param string $message
 */
function assertStringEndsWith($suffix, $string, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertStringEndsWith',
        func_get_args()
    );
}

/**
 * Asserts that the contents of a string is equal
 * to the contents of a file.
 *
 * @param string $expectedFile
 * @param string $actualString
 * @param string $message
 * @param bool   $canonicalize
 * @param bool   $ignoreCase
 */
function assertStringEqualsFile($expectedFile, $actualString, $message = '', $canonicalize = false, $ignoreCase = false)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertStringEqualsFile',
        func_get_args()
    );
}

/**
 * Asserts that a string matches a given format string.
 *
 * @param string $format
 * @param string $string
 * @param string $message
 */
function assertStringMatchesFormat($format, $string, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertStringMatchesFormat',
        func_get_args()
    );
}

/**
 * Asserts that a string matches a given format file.
 *
 * @param string $formatFile
 * @param string $string
 * @param string $message
 */
function assertStringMatchesFormatFile($formatFile, $string, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertStringMatchesFormatFile',
        func_get_args()
    );
}

/**
 * Asserts that the contents of a string is not equal
 * to the contents of a file.
 *
 * @param string $expectedFile
 * @param string $actualString
 * @param string $message
 * @param bool   $canonicalize
 * @param bool   $ignoreCase
 */
function assertStringNotEqualsFile($expectedFile, $actualString, $message = '', $canonicalize = false, $ignoreCase = false)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertStringNotEqualsFile',
        func_get_args()
    );
}

/**
 * Asserts that a string does not match a given format string.
 *
 * @param string $format
 * @param string $string
 * @param string $message
 */
function assertStringNotMatchesFormat($format, $string, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertStringNotMatchesFormat',
        func_get_args()
    );
}

/**
 * Asserts that a string does not match a given format string.
 *
 * @param string $formatFile
 * @param string $string
 * @param string $message
 */
function assertStringNotMatchesFormatFile($formatFile, $string, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertStringNotMatchesFormatFile',
        func_get_args()
    );
}

/**
 * Asserts that a string starts not with a given prefix.
 *
 * @param string $prefix
 * @param string $string
 * @param string $message
 */
function assertStringStartsNotWith($prefix, $string, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertStringStartsNotWith',
        func_get_args()
    );
}

/**
 * Asserts that a string starts with a given prefix.
 *
 * @param string $prefix
 * @param string $string
 * @param string $message
 */
function assertStringStartsWith($prefix, $string, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertStringStartsWith',
        func_get_args()
    );
}

/**
 * Evaluates a PHPUnit_Framework_Constraint matcher object.
 *
 * @param mixed                        $value
 * @param PHPUnit_Framework_Constraint $constraint
 * @param string                       $message
 */
function assertThat($value, PHPUnit_Framework_Constraint $constraint, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertThat',
        func_get_args()
    );
}

/**
 * Asserts that a condition is true.
 *
 * @param bool   $condition
 * @param string $message
 *
 * @throws PHPUnit_Framework_AssertionFailedError
 */
function assertTrue($condition, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertTrue',
        func_get_args()
    );
}

/**
 * Asserts that two XML files are equal.
 *
 * @param string $expectedFile
 * @param string $actualFile
 * @param string $message
 */
function assertXmlFileEqualsXmlFile($expectedFile, $actualFile, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertXmlFileEqualsXmlFile',
        func_get_args()
    );
}

/**
 * Asserts that two XML files are not equal.
 *
 * @param string $expectedFile
 * @param string $actualFile
 * @param string $message
 */
function assertXmlFileNotEqualsXmlFile($expectedFile, $actualFile, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertXmlFileNotEqualsXmlFile',
        func_get_args()
    );
}

/**
 * Asserts that two XML documents are equal.
 *
 * @param string $expectedFile
 * @param string $actualXml
 * @param string $message
 */
function assertXmlStringEqualsXmlFile($expectedFile, $actualXml, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertXmlStringEqualsXmlFile',
        func_get_args()
    );
}

/**
 * Asserts that two XML documents are equal.
 *
 * @param string $expectedXml
 * @param string $actualXml
 * @param string $message
 */
function assertXmlStringEqualsXmlString($expectedXml, $actualXml, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertXmlStringEqualsXmlString',
        func_get_args()
    );
}

/**
 * Asserts that two XML documents are not equal.
 *
 * @param string $expectedFile
 * @param string $actualXml
 * @param string $message
 */
function assertXmlStringNotEqualsXmlFile($expectedFile, $actualXml, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertXmlStringNotEqualsXmlFile',
        func_get_args()
    );
}

/**
 * Asserts that two XML documents are not equal.
 *
 * @param string $expectedXml
 * @param string $actualXml
 * @param string $message
 */
function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, $message = '')
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::assertXmlStringNotEqualsXmlString',
        func_get_args()
    );
}

/**
 * Returns a matcher that matches when the method is executed
 * at the given $index.
 *
 * @param int $index
 *
 * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex
 */
function at($index)
{
    return call_user_func_array(
        'PHPUnit_Framework_TestCase::at',
        func_get_args()
    );
}

/**
 * Returns a matcher that matches when the method is executed at least once.
 *
 * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce
 */
function atLeastOnce()
{
    return call_user_func_array(
        'PHPUnit_Framework_TestCase::atLeastOnce',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_Attribute matcher object.
 *
 * @param PHPUnit_Framework_Constraint $constraint
 * @param string                       $attributeName
 *
 * @return PHPUnit_Framework_Constraint_Attribute
 */
function attribute(PHPUnit_Framework_Constraint $constraint, $attributeName)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::attribute',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_IsEqual matcher object
 * that is wrapped in a PHPUnit_Framework_Constraint_Attribute matcher
 * object.
 *
 * @param string $attributeName
 * @param mixed  $value
 * @param float  $delta
 * @param int    $maxDepth
 * @param bool   $canonicalize
 * @param bool   $ignoreCase
 *
 * @return PHPUnit_Framework_Constraint_Attribute
 */
function attributeEqualTo($attributeName, $value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::attributeEqualTo',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_Callback matcher object.
 *
 * @param callable $callback
 *
 * @return PHPUnit_Framework_Constraint_Callback
 */
function callback($callback)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::callback',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_ClassHasAttribute matcher object.
 *
 * @param string $attributeName
 *
 * @return PHPUnit_Framework_Constraint_ClassHasAttribute
 */
function classHasAttribute($attributeName)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::classHasAttribute',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_ClassHasStaticAttribute matcher
 * object.
 *
 * @param string $attributeName
 *
 * @return PHPUnit_Framework_Constraint_ClassHasStaticAttribute
 */
function classHasStaticAttribute($attributeName)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::classHasStaticAttribute',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_TraversableContains matcher
 * object.
 *
 * @param mixed $value
 * @param bool  $checkForObjectIdentity
 * @param bool  $checkForNonObjectIdentity
 *
 * @return PHPUnit_Framework_Constraint_TraversableContains
 */
function contains($value, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::contains',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_TraversableContainsOnly matcher
 * object.
 *
 * @param string $type
 *
 * @return PHPUnit_Framework_Constraint_TraversableContainsOnly
 */
function containsOnly($type)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::containsOnly',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_TraversableContainsOnly matcher
 * object.
 *
 * @param string $classname
 *
 * @return PHPUnit_Framework_Constraint_TraversableContainsOnly
 */
function containsOnlyInstancesOf($classname)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::containsOnlyInstancesOf',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_Count matcher object.
 *
 * @param int $count
 *
 * @return Count
 */
function countOf($count)
{
    return call_user_func_array(
        'PHPUnit\Framework\Assert::countOf',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_IsEqual matcher object.
 *
 * @param mixed $value
 * @param float $delta
 * @param int   $maxDepth
 * @param bool  $canonicalize
 * @param bool  $ignoreCase
 *
 * @return PHPUnit_Framework_Constraint_IsEqual
 */
function equalTo($value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::equalTo',
        func_get_args()
    );
}

/**
 * Returns a matcher that matches when the method is executed
 * exactly $count times.
 *
 * @param int $count
 *
 * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount
 */
function exactly($count)
{
    return call_user_func_array(
        'PHPUnit_Framework_TestCase::exactly',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_FileExists matcher object.
 *
 * @return PHPUnit_Framework_Constraint_FileExists
 */
function fileExists()
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::fileExists',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_GreaterThan matcher object.
 *
 * @param mixed $value
 *
 * @return PHPUnit_Framework_Constraint_GreaterThan
 */
function greaterThan($value)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::greaterThan',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_Or matcher object that wraps
 * a PHPUnit_Framework_Constraint_IsEqual and a
 * PHPUnit_Framework_Constraint_GreaterThan matcher object.
 *
 * @param mixed $value
 *
 * @return PHPUnit_Framework_Constraint_Or
 */
function greaterThanOrEqual($value)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::greaterThanOrEqual',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_IsIdentical matcher object.
 *
 * @param mixed $value
 *
 * @return PHPUnit_Framework_Constraint_IsIdentical
 */
function identicalTo($value)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::identicalTo',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_IsEmpty matcher object.
 *
 * @return PHPUnit_Framework_Constraint_IsEmpty
 */
function isEmpty()
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::isEmpty',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_IsFalse matcher object.
 *
 * @return PHPUnit_Framework_Constraint_IsFalse
 */
function isFalse()
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::isFalse',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_IsInstanceOf matcher object.
 *
 * @param string $className
 *
 * @return PHPUnit_Framework_Constraint_IsInstanceOf
 */
function isInstanceOf($className)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::isInstanceOf',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_IsJson matcher object.
 *
 * @return PHPUnit_Framework_Constraint_IsJson
 */
function isJson()
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::isJson',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_IsNull matcher object.
 *
 * @return PHPUnit_Framework_Constraint_IsNull
 */
function isNull()
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::isNull',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_IsTrue matcher object.
 *
 * @return PHPUnit_Framework_Constraint_IsTrue
 */
function isTrue()
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::isTrue',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_IsType matcher object.
 *
 * @param string $type
 *
 * @return PHPUnit_Framework_Constraint_IsType
 */
function isType($type)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::isType',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_LessThan matcher object.
 *
 * @param mixed $value
 *
 * @return PHPUnit_Framework_Constraint_LessThan
 */
function lessThan($value)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::lessThan',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_Or matcher object that wraps
 * a PHPUnit_Framework_Constraint_IsEqual and a
 * PHPUnit_Framework_Constraint_LessThan matcher object.
 *
 * @param mixed $value
 *
 * @return PHPUnit_Framework_Constraint_Or
 */
function lessThanOrEqual($value)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::lessThanOrEqual',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_And matcher object.
 *
 * @return PHPUnit_Framework_Constraint_And
 */
function logicalAnd()
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::logicalAnd',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_Not matcher object.
 *
 * @param PHPUnit_Framework_Constraint $constraint
 *
 * @return PHPUnit_Framework_Constraint_Not
 */
function logicalNot(PHPUnit_Framework_Constraint $constraint)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::logicalNot',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_Or matcher object.
 *
 * @return PHPUnit_Framework_Constraint_Or
 */
function logicalOr()
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::logicalOr',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_Xor matcher object.
 *
 * @return PHPUnit_Framework_Constraint_Xor
 */
function logicalXor()
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::logicalXor',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_StringMatches matcher object.
 *
 * @param string $string
 *
 * @return PHPUnit_Framework_Constraint_StringMatches
 */
function matches($string)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::matches',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_PCREMatch matcher object.
 *
 * @param string $pattern
 *
 * @return PHPUnit_Framework_Constraint_PCREMatch
 */
function matchesRegularExpression($pattern)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::matchesRegularExpression',
        func_get_args()
    );
}

/**
 * Returns a matcher that matches when the method is never executed.
 *
 * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount
 */
function never()
{
    return call_user_func_array(
        'PHPUnit_Framework_TestCase::never',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_ObjectHasAttribute matcher object.
 *
 * @param string $attributeName
 *
 * @return PHPUnit_Framework_Constraint_ObjectHasAttribute
 */
function objectHasAttribute($attributeName)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::objectHasAttribute',
        func_get_args()
    );
}

/**
 * @param mixed $value, ...
 *
 * @return PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls
 */
function onConsecutiveCalls()
{
    return call_user_func_array(
        'PHPUnit_Framework_TestCase::onConsecutiveCalls',
        func_get_args()
    );
}

/**
 * Returns a matcher that matches when the method is executed exactly once.
 *
 * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount
 */
function once()
{
    return call_user_func_array(
        'PHPUnit_Framework_TestCase::once',
        func_get_args()
    );
}

/**
 * @param int $argumentIndex
 *
 * @return PHPUnit_Framework_MockObject_Stub_ReturnArgument
 */
function returnArgument($argumentIndex)
{
    return call_user_func_array(
        'PHPUnit_Framework_TestCase::returnArgument',
        func_get_args()
    );
}

/**
 * @param mixed $callback
 *
 * @return PHPUnit_Framework_MockObject_Stub_ReturnCallback
 */
function returnCallback($callback)
{
    return call_user_func_array(
        'PHPUnit_Framework_TestCase::returnCallback',
        func_get_args()
    );
}

/**
 * Returns the current object.
 *
 * This method is useful when mocking a fluent interface.
 *
 * @return PHPUnit_Framework_MockObject_Stub_ReturnSelf
 */
function returnSelf()
{
    return call_user_func_array(
        'PHPUnit_Framework_TestCase::returnSelf',
        func_get_args()
    );
}

/**
 * @param mixed $value
 *
 * @return PHPUnit_Framework_MockObject_Stub_Return
 */
function returnValue($value)
{
    return call_user_func_array(
        'PHPUnit_Framework_TestCase::returnValue',
        func_get_args()
    );
}

/**
 * @param array $valueMap
 *
 * @return PHPUnit_Framework_MockObject_Stub_ReturnValueMap
 */
function returnValueMap(array $valueMap)
{
    return call_user_func_array(
        'PHPUnit_Framework_TestCase::returnValueMap',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_StringContains matcher object.
 *
 * @param string $string
 * @param bool   $case
 *
 * @return PHPUnit_Framework_Constraint_StringContains
 */
function stringContains($string, $case = true)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::stringContains',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_StringEndsWith matcher object.
 *
 * @param mixed $suffix
 *
 * @return PHPUnit_Framework_Constraint_StringEndsWith
 */
function stringEndsWith($suffix)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::stringEndsWith',
        func_get_args()
    );
}

/**
 * Returns a PHPUnit_Framework_Constraint_StringStartsWith matcher object.
 *
 * @param mixed $prefix
 *
 * @return PHPUnit_Framework_Constraint_StringStartsWith
 */
function stringStartsWith($prefix)
{
    return call_user_func_array(
        'PHPUnit_Framework_Assert::stringStartsWith',
        func_get_args()
    );
}

/**
 * @param Exception $exception
 *
 * @return PHPUnit_Framework_MockObject_Stub_Exception
 */
function throwException(Exception $exception)
{
    return call_user_func_array(
        'PHPUnit_Framework_TestCase::throwException',
        func_get_args()
    );
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A set of assertion methods.
 */
abstract class PHPUnit_Framework_Assert
{
    /**
     * @var int
     */
    private static $count = 0;

    /**
     * Asserts that an array has a specified key.
     *
     * @param mixed             $key
     * @param array|ArrayAccess $array
     * @param string            $message
     */
    public static function assertArrayHasKey($key, $array, $message = '')
    {
        if (!(is_int($key) || is_string($key))) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(
                1,
                'integer or string'
            );
        }

        if (!(is_array($array) || $array instanceof ArrayAccess)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(
                2,
                'array or ArrayAccess'
            );
        }

        $constraint = new PHPUnit_Framework_Constraint_ArrayHasKey($key);

        static::assertThat($array, $constraint, $message);
    }

    /**
     * Asserts that an array has a specified subset.
     *
     * @param array|ArrayAccess $subset
     * @param array|ArrayAccess $array
     * @param bool              $strict  Check for object identity
     * @param string            $message
     */
    public static function assertArraySubset($subset, $array, $strict = false, $message = '')
    {
        if (!(is_array($subset) || $subset instanceof ArrayAccess)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(
                1,
                'array or ArrayAccess'
            );
        }

        if (!(is_array($array) || $array instanceof ArrayAccess)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(
                2,
                'array or ArrayAccess'
            );
        }

        $constraint = new PHPUnit_Framework_Constraint_ArraySubset($subset, $strict);

        static::assertThat($array, $constraint, $message);
    }

    /**
     * Asserts that an array does not have a specified key.
     *
     * @param mixed             $key
     * @param array|ArrayAccess $array
     * @param string            $message
     */
    public static function assertArrayNotHasKey($key, $array, $message = '')
    {
        if (!(is_int($key) || is_string($key))) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(
                1,
                'integer or string'
            );
        }

        if (!(is_array($array) || $array instanceof ArrayAccess)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(
                2,
                'array or ArrayAccess'
            );
        }

        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_ArrayHasKey($key)
        );

        static::assertThat($array, $constraint, $message);
    }

    /**
     * Asserts that a haystack contains a needle.
     *
     * @param mixed  $needle
     * @param mixed  $haystack
     * @param string $message
     * @param bool   $ignoreCase
     * @param bool   $checkForObjectIdentity
     * @param bool   $checkForNonObjectIdentity
     */
    public static function assertContains($needle, $haystack, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
    {
        if (is_array($haystack) ||
            is_object($haystack) && $haystack instanceof Traversable) {
            $constraint = new PHPUnit_Framework_Constraint_TraversableContains(
                $needle,
                $checkForObjectIdentity,
                $checkForNonObjectIdentity
            );
        } elseif (is_string($haystack)) {
            if (!is_string($needle)) {
                throw PHPUnit_Util_InvalidArgumentHelper::factory(
                    1,
                    'string'
                );
            }

            $constraint = new PHPUnit_Framework_Constraint_StringContains(
                $needle,
                $ignoreCase
            );
        } else {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(
                2,
                'array, traversable or string'
            );
        }

        static::assertThat($haystack, $constraint, $message);
    }

    /**
     * Asserts that a haystack that is stored in a static attribute of a class
     * or an attribute of an object contains a needle.
     *
     * @param mixed         $needle
     * @param string        $haystackAttributeName
     * @param string|object $haystackClassOrObject
     * @param string        $message
     * @param bool          $ignoreCase
     * @param bool          $checkForObjectIdentity
     * @param bool          $checkForNonObjectIdentity
     */
    public static function assertAttributeContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
    {
        static::assertContains(
            $needle,
            static::readAttribute($haystackClassOrObject, $haystackAttributeName),
            $message,
            $ignoreCase,
            $checkForObjectIdentity,
            $checkForNonObjectIdentity
        );
    }

    /**
     * Asserts that a haystack does not contain a needle.
     *
     * @param mixed  $needle
     * @param mixed  $haystack
     * @param string $message
     * @param bool   $ignoreCase
     * @param bool   $checkForObjectIdentity
     * @param bool   $checkForNonObjectIdentity
     */
    public static function assertNotContains($needle, $haystack, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
    {
        if (is_array($haystack) ||
            is_object($haystack) && $haystack instanceof Traversable) {
            $constraint = new PHPUnit_Framework_Constraint_Not(
                new PHPUnit_Framework_Constraint_TraversableContains(
                    $needle,
                    $checkForObjectIdentity,
                    $checkForNonObjectIdentity
                )
            );
        } elseif (is_string($haystack)) {
            if (!is_string($needle)) {
                throw PHPUnit_Util_InvalidArgumentHelper::factory(
                    1,
                    'string'
                );
            }

            $constraint = new PHPUnit_Framework_Constraint_Not(
                new PHPUnit_Framework_Constraint_StringContains(
                    $needle,
                    $ignoreCase
                )
            );
        } else {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(
                2,
                'array, traversable or string'
            );
        }

        static::assertThat($haystack, $constraint, $message);
    }

    /**
     * Asserts that a haystack that is stored in a static attribute of a class
     * or an attribute of an object does not contain a needle.
     *
     * @param mixed         $needle
     * @param string        $haystackAttributeName
     * @param string|object $haystackClassOrObject
     * @param string        $message
     * @param bool          $ignoreCase
     * @param bool          $checkForObjectIdentity
     * @param bool          $checkForNonObjectIdentity
     */
    public static function assertAttributeNotContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
    {
        static::assertNotContains(
            $needle,
            static::readAttribute($haystackClassOrObject, $haystackAttributeName),
            $message,
            $ignoreCase,
            $checkForObjectIdentity,
            $checkForNonObjectIdentity
        );
    }

    /**
     * Asserts that a haystack contains only values of a given type.
     *
     * @param string $type
     * @param mixed  $haystack
     * @param bool   $isNativeType
     * @param string $message
     */
    public static function assertContainsOnly($type, $haystack, $isNativeType = null, $message = '')
    {
        if (!(is_array($haystack) ||
            is_object($haystack) && $haystack instanceof Traversable)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(
                2,
                'array or traversable'
            );
        }

        if ($isNativeType == null) {
            $isNativeType = PHPUnit_Util_Type::isType($type);
        }

        static::assertThat(
            $haystack,
            new PHPUnit_Framework_Constraint_TraversableContainsOnly(
                $type,
                $isNativeType
            ),
            $message
        );
    }

    /**
     * Asserts that a haystack contains only instances of a given classname
     *
     * @param string            $classname
     * @param array|Traversable $haystack
     * @param string            $message
     */
    public static function assertContainsOnlyInstancesOf($classname, $haystack, $message = '')
    {
        if (!(is_array($haystack) ||
            is_object($haystack) && $haystack instanceof Traversable)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(
                2,
                'array or traversable'
            );
        }

        static::assertThat(
            $haystack,
            new PHPUnit_Framework_Constraint_TraversableContainsOnly(
                $classname,
                false
            ),
            $message
        );
    }

    /**
     * Asserts that a haystack that is stored in a static attribute of a class
     * or an attribute of an object contains only values of a given type.
     *
     * @param string        $type
     * @param string        $haystackAttributeName
     * @param string|object $haystackClassOrObject
     * @param bool          $isNativeType
     * @param string        $message
     */
    public static function assertAttributeContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = null, $message = '')
    {
        static::assertContainsOnly(
            $type,
            static::readAttribute($haystackClassOrObject, $haystackAttributeName),
            $isNativeType,
            $message
        );
    }

    /**
     * Asserts that a haystack does not contain only values of a given type.
     *
     * @param string $type
     * @param mixed  $haystack
     * @param bool   $isNativeType
     * @param string $message
     */
    public static function assertNotContainsOnly($type, $haystack, $isNativeType = null, $message = '')
    {
        if (!(is_array($haystack) ||
            is_object($haystack) && $haystack instanceof Traversable)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(
                2,
                'array or traversable'
            );
        }

        if ($isNativeType == null) {
            $isNativeType = PHPUnit_Util_Type::isType($type);
        }

        static::assertThat(
            $haystack,
            new PHPUnit_Framework_Constraint_Not(
                new PHPUnit_Framework_Constraint_TraversableContainsOnly(
                    $type,
                    $isNativeType
                )
            ),
            $message
        );
    }

    /**
     * Asserts that a haystack that is stored in a static attribute of a class
     * or an attribute of an object does not contain only values of a given
     * type.
     *
     * @param string        $type
     * @param string        $haystackAttributeName
     * @param string|object $haystackClassOrObject
     * @param bool          $isNativeType
     * @param string        $message
     */
    public static function assertAttributeNotContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = null, $message = '')
    {
        static::assertNotContainsOnly(
            $type,
            static::readAttribute($haystackClassOrObject, $haystackAttributeName),
            $isNativeType,
            $message
        );
    }

    /**
     * Asserts the number of elements of an array, Countable or Traversable.
     *
     * @param int    $expectedCount
     * @param mixed  $haystack
     * @param string $message
     */
    public static function assertCount($expectedCount, $haystack, $message = '')
    {
        if (!is_int($expectedCount)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer');
        }

        if (!$haystack instanceof Countable &&
            !$haystack instanceof Traversable &&
            !is_array($haystack)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'countable or traversable');
        }

        static::assertThat(
            $haystack,
            new PHPUnit_Framework_Constraint_Count($expectedCount),
            $message
        );
    }

    /**
     * Asserts the number of elements of an array, Countable or Traversable
     * that is stored in an attribute.
     *
     * @param int           $expectedCount
     * @param string        $haystackAttributeName
     * @param string|object $haystackClassOrObject
     * @param string        $message
     */
    public static function assertAttributeCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message = '')
    {
        static::assertCount(
            $expectedCount,
            static::readAttribute($haystackClassOrObject, $haystackAttributeName),
            $message
        );
    }

    /**
     * Asserts the number of elements of an array, Countable or Traversable.
     *
     * @param int    $expectedCount
     * @param mixed  $haystack
     * @param string $message
     */
    public static function assertNotCount($expectedCount, $haystack, $message = '')
    {
        if (!is_int($expectedCount)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer');
        }

        if (!$haystack instanceof Countable &&
            !$haystack instanceof Traversable &&
            !is_array($haystack)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'countable or traversable');
        }

        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_Count($expectedCount)
        );

        static::assertThat($haystack, $constraint, $message);
    }

    /**
     * Asserts the number of elements of an array, Countable or Traversable
     * that is stored in an attribute.
     *
     * @param int           $expectedCount
     * @param string        $haystackAttributeName
     * @param string|object $haystackClassOrObject
     * @param string        $message
     */
    public static function assertAttributeNotCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message = '')
    {
        static::assertNotCount(
            $expectedCount,
            static::readAttribute($haystackClassOrObject, $haystackAttributeName),
            $message
        );
    }

    /**
     * Asserts that two variables are equal.
     *
     * @param mixed  $expected
     * @param mixed  $actual
     * @param string $message
     * @param float  $delta
     * @param int    $maxDepth
     * @param bool   $canonicalize
     * @param bool   $ignoreCase
     */
    public static function assertEquals($expected, $actual, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
    {
        $constraint = new PHPUnit_Framework_Constraint_IsEqual(
            $expected,
            $delta,
            $maxDepth,
            $canonicalize,
            $ignoreCase
        );

        static::assertThat($actual, $constraint, $message);
    }

    /**
     * Asserts that a variable is equal to an attribute of an object.
     *
     * @param mixed         $expected
     * @param string        $actualAttributeName
     * @param string|object $actualClassOrObject
     * @param string        $message
     * @param float         $delta
     * @param int           $maxDepth
     * @param bool          $canonicalize
     * @param bool          $ignoreCase
     */
    public static function assertAttributeEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
    {
        static::assertEquals(
            $expected,
            static::readAttribute($actualClassOrObject, $actualAttributeName),
            $message,
            $delta,
            $maxDepth,
            $canonicalize,
            $ignoreCase
        );
    }

    /**
     * Asserts that two variables are not equal.
     *
     * @param mixed  $expected
     * @param mixed  $actual
     * @param string $message
     * @param float  $delta
     * @param int    $maxDepth
     * @param bool   $canonicalize
     * @param bool   $ignoreCase
     */
    public static function assertNotEquals($expected, $actual, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
    {
        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_IsEqual(
                $expected,
                $delta,
                $maxDepth,
                $canonicalize,
                $ignoreCase
            )
        );

        static::assertThat($actual, $constraint, $message);
    }

    /**
     * Asserts that a variable is not equal to an attribute of an object.
     *
     * @param mixed         $expected
     * @param string        $actualAttributeName
     * @param string|object $actualClassOrObject
     * @param string        $message
     * @param float         $delta
     * @param int           $maxDepth
     * @param bool          $canonicalize
     * @param bool          $ignoreCase
     */
    public static function assertAttributeNotEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
    {
        static::assertNotEquals(
            $expected,
            static::readAttribute($actualClassOrObject, $actualAttributeName),
            $message,
            $delta,
            $maxDepth,
            $canonicalize,
            $ignoreCase
        );
    }

    /**
     * Asserts that a variable is empty.
     *
     * @param mixed  $actual
     * @param string $message
     *
     * @throws PHPUnit_Framework_AssertionFailedError
     */
    public static function assertEmpty($actual, $message = '')
    {
        static::assertThat($actual, static::isEmpty(), $message);
    }

    /**
     * Asserts that a static attribute of a class or an attribute of an object
     * is empty.
     *
     * @param string        $haystackAttributeName
     * @param string|object $haystackClassOrObject
     * @param string        $message
     */
    public static function assertAttributeEmpty($haystackAttributeName, $haystackClassOrObject, $message = '')
    {
        static::assertEmpty(
            static::readAttribute($haystackClassOrObject, $haystackAttributeName),
            $message
        );
    }

    /**
     * Asserts that a variable is not empty.
     *
     * @param mixed  $actual
     * @param string $message
     *
     * @throws PHPUnit_Framework_AssertionFailedError
     */
    public static function assertNotEmpty($actual, $message = '')
    {
        static::assertThat($actual, static::logicalNot(static::isEmpty()), $message);
    }

    /**
     * Asserts that a static attribute of a class or an attribute of an object
     * is not empty.
     *
     * @param string        $haystackAttributeName
     * @param string|object $haystackClassOrObject
     * @param string        $message
     */
    public static function assertAttributeNotEmpty($haystackAttributeName, $haystackClassOrObject, $message = '')
    {
        static::assertNotEmpty(
            static::readAttribute($haystackClassOrObject, $haystackAttributeName),
            $message
        );
    }

    /**
     * Asserts that a value is greater than another value.
     *
     * @param mixed  $expected
     * @param mixed  $actual
     * @param string $message
     */
    public static function assertGreaterThan($expected, $actual, $message = '')
    {
        static::assertThat($actual, static::greaterThan($expected), $message);
    }

    /**
     * Asserts that an attribute is greater than another value.
     *
     * @param mixed         $expected
     * @param string        $actualAttributeName
     * @param string|object $actualClassOrObject
     * @param string        $message
     */
    public static function assertAttributeGreaterThan($expected, $actualAttributeName, $actualClassOrObject, $message = '')
    {
        static::assertGreaterThan(
            $expected,
            static::readAttribute($actualClassOrObject, $actualAttributeName),
            $message
        );
    }

    /**
     * Asserts that a value is greater than or equal to another value.
     *
     * @param mixed  $expected
     * @param mixed  $actual
     * @param string $message
     */
    public static function assertGreaterThanOrEqual($expected, $actual, $message = '')
    {
        static::assertThat(
            $actual,
            static::greaterThanOrEqual($expected),
            $message
        );
    }

    /**
     * Asserts that an attribute is greater than or equal to another value.
     *
     * @param mixed         $expected
     * @param string        $actualAttributeName
     * @param string|object $actualClassOrObject
     * @param string        $message
     */
    public static function assertAttributeGreaterThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '')
    {
        static::assertGreaterThanOrEqual(
            $expected,
            static::readAttribute($actualClassOrObject, $actualAttributeName),
            $message
        );
    }

    /**
     * Asserts that a value is smaller than another value.
     *
     * @param mixed  $expected
     * @param mixed  $actual
     * @param string $message
     */
    public static function assertLessThan($expected, $actual, $message = '')
    {
        static::assertThat($actual, static::lessThan($expected), $message);
    }

    /**
     * Asserts that an attribute is smaller than another value.
     *
     * @param mixed         $expected
     * @param string        $actualAttributeName
     * @param string|object $actualClassOrObject
     * @param string        $message
     */
    public static function assertAttributeLessThan($expected, $actualAttributeName, $actualClassOrObject, $message = '')
    {
        static::assertLessThan(
            $expected,
            static::readAttribute($actualClassOrObject, $actualAttributeName),
            $message
        );
    }

    /**
     * Asserts that a value is smaller than or equal to another value.
     *
     * @param mixed  $expected
     * @param mixed  $actual
     * @param string $message
     */
    public static function assertLessThanOrEqual($expected, $actual, $message = '')
    {
        static::assertThat($actual, static::lessThanOrEqual($expected), $message);
    }

    /**
     * Asserts that an attribute is smaller than or equal to another value.
     *
     * @param mixed         $expected
     * @param string        $actualAttributeName
     * @param string|object $actualClassOrObject
     * @param string        $message
     */
    public static function assertAttributeLessThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '')
    {
        static::assertLessThanOrEqual(
            $expected,
            static::readAttribute($actualClassOrObject, $actualAttributeName),
            $message
        );
    }

    /**
     * Asserts that the contents of one file is equal to the contents of another
     * file.
     *
     * @param string $expected
     * @param string $actual
     * @param string $message
     * @param bool   $canonicalize
     * @param bool   $ignoreCase
     */
    public static function assertFileEquals($expected, $actual, $message = '', $canonicalize = false, $ignoreCase = false)
    {
        static::assertFileExists($expected, $message);
        static::assertFileExists($actual, $message);

        static::assertEquals(
            file_get_contents($expected),
            file_get_contents($actual),
            $message,
            0,
            10,
            $canonicalize,
            $ignoreCase
        );
    }

    /**
     * Asserts that the contents of one file is not equal to the contents of
     * another file.
     *
     * @param string $expected
     * @param string $actual
     * @param string $message
     * @param bool   $canonicalize
     * @param bool   $ignoreCase
     */
    public static function assertFileNotEquals($expected, $actual, $message = '', $canonicalize = false, $ignoreCase = false)
    {
        static::assertFileExists($expected, $message);
        static::assertFileExists($actual, $message);

        static::assertNotEquals(
            file_get_contents($expected),
            file_get_contents($actual),
            $message,
            0,
            10,
            $canonicalize,
            $ignoreCase
        );
    }

    /**
     * Asserts that the contents of a string is equal
     * to the contents of a file.
     *
     * @param string $expectedFile
     * @param string $actualString
     * @param string $message
     * @param bool   $canonicalize
     * @param bool   $ignoreCase
     */
    public static function assertStringEqualsFile($expectedFile, $actualString, $message = '', $canonicalize = false, $ignoreCase = false)
    {
        static::assertFileExists($expectedFile, $message);

        static::assertEquals(
            file_get_contents($expectedFile),
            $actualString,
            $message,
            0,
            10,
            $canonicalize,
            $ignoreCase
        );
    }

    /**
     * Asserts that the contents of a string is not equal
     * to the contents of a file.
     *
     * @param string $expectedFile
     * @param string $actualString
     * @param string $message
     * @param bool   $canonicalize
     * @param bool   $ignoreCase
     */
    public static function assertStringNotEqualsFile($expectedFile, $actualString, $message = '', $canonicalize = false, $ignoreCase = false)
    {
        static::assertFileExists($expectedFile, $message);

        static::assertNotEquals(
            file_get_contents($expectedFile),
            $actualString,
            $message,
            0,
            10,
            $canonicalize,
            $ignoreCase
        );
    }

    /**
     * Asserts that a file/dir is readable.
     *
     * @param string $filename
     * @param string $message
     */
    public static function assertIsReadable($filename, $message = '')
    {
        if (!is_string($filename)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_IsReadable;

        static::assertThat($filename, $constraint, $message);
    }

    /**
     * Asserts that a file/dir exists and is not readable.
     *
     * @param string $filename
     * @param string $message
     */
    public static function assertNotIsReadable($filename, $message = '')
    {
        if (!is_string($filename)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_IsReadable
        );

        static::assertThat($filename, $constraint, $message);
    }

    /**
     * Asserts that a file/dir exists and is writable.
     *
     * @param string $filename
     * @param string $message
     */
    public static function assertIsWritable($filename, $message = '')
    {
        if (!is_string($filename)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_IsWritable;

        static::assertThat($filename, $constraint, $message);
    }

    /**
     * Asserts that a file/dir exists and is not writable.
     *
     * @param string $filename
     * @param string $message
     */
    public static function assertNotIsWritable($filename, $message = '')
    {
        if (!is_string($filename)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_IsWritable
        );

        static::assertThat($filename, $constraint, $message);
    }

    /**
     * Asserts that a directory exists.
     *
     * @param string $directory
     * @param string $message
     */
    public static function assertDirectoryExists($directory, $message = '')
    {
        if (!is_string($directory)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_DirectoryExists;

        static::assertThat($directory, $constraint, $message);
    }

    /**
     * Asserts that a directory does not exist.
     *
     * @param string $directory
     * @param string $message
     */
    public static function assertDirectoryNotExists($directory, $message = '')
    {
        if (!is_string($directory)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_DirectoryExists
        );

        static::assertThat($directory, $constraint, $message);
    }

    /**
     * Asserts that a directory exists and is readable.
     *
     * @param string $directory
     * @param string $message
     */
    public static function assertDirectoryIsReadable($directory, $message = '')
    {
        self::assertDirectoryExists($directory, $message);
        self::assertIsReadable($directory, $message);
    }

    /**
     * Asserts that a directory exists and is not readable.
     *
     * @param string $directory
     * @param string $message
     */
    public static function assertDirectoryNotIsReadable($directory, $message = '')
    {
        self::assertDirectoryExists($directory, $message);
        self::assertNotIsReadable($directory, $message);
    }

    /**
     * Asserts that a directory exists and is writable.
     *
     * @param string $directory
     * @param string $message
     */
    public static function assertDirectoryIsWritable($directory, $message = '')
    {
        self::assertDirectoryExists($directory, $message);
        self::assertIsWritable($directory, $message);
    }

    /**
     * Asserts that a directory exists and is not writable.
     *
     * @param string $directory
     * @param string $message
     */
    public static function assertDirectoryNotIsWritable($directory, $message = '')
    {
        self::assertDirectoryExists($directory, $message);
        self::assertNotIsWritable($directory, $message);
    }

    /**
     * Asserts that a file exists.
     *
     * @param string $filename
     * @param string $message
     */
    public static function assertFileExists($filename, $message = '')
    {
        if (!is_string($filename)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_FileExists;

        static::assertThat($filename, $constraint, $message);
    }

    /**
     * Asserts that a file does not exist.
     *
     * @param string $filename
     * @param string $message
     */
    public static function assertFileNotExists($filename, $message = '')
    {
        if (!is_string($filename)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_FileExists
        );

        static::assertThat($filename, $constraint, $message);
    }

    /**
     * Asserts that a file exists and is readable.
     *
     * @param string $file
     * @param string $message
     */
    public static function assertFileIsReadable($file, $message = '')
    {
        self::assertFileExists($file, $message);
        self::assertIsReadable($file, $message);
    }

    /**
     * Asserts that a file exists and is not readable.
     *
     * @param string $file
     * @param string $message
     */
    public static function assertFileNotIsReadable($file, $message = '')
    {
        self::assertFileExists($file, $message);
        self::assertNotIsReadable($file, $message);
    }

    /**
     * Asserts that a file exists and is writable.
     *
     * @param string $file
     * @param string $message
     */
    public static function assertFileIsWritable($file, $message = '')
    {
        self::assertFileExists($file, $message);
        self::assertIsWritable($file, $message);
    }

    /**
     * Asserts that a file exists and is not writable.
     *
     * @param string $file
     * @param string $message
     */
    public static function assertFileNotIsWritable($file, $message = '')
    {
        self::assertFileExists($file, $message);
        self::assertNotIsWritable($file, $message);
    }

    /**
     * Asserts that a condition is true.
     *
     * @param bool   $condition
     * @param string $message
     *
     * @throws PHPUnit_Framework_AssertionFailedError
     */
    public static function assertTrue($condition, $message = '')
    {
        static::assertThat($condition, static::isTrue(), $message);
    }

    /**
     * Asserts that a condition is not true.
     *
     * @param bool   $condition
     * @param string $message
     *
     * @throws PHPUnit_Framework_AssertionFailedError
     */
    public static function assertNotTrue($condition, $message = '')
    {
        static::assertThat($condition, static::logicalNot(static::isTrue()), $message);
    }

    /**
     * Asserts that a condition is false.
     *
     * @param bool   $condition
     * @param string $message
     *
     * @throws PHPUnit_Framework_AssertionFailedError
     */
    public static function assertFalse($condition, $message = '')
    {
        static::assertThat($condition, static::isFalse(), $message);
    }

    /**
     * Asserts that a condition is not false.
     *
     * @param bool   $condition
     * @param string $message
     *
     * @throws PHPUnit_Framework_AssertionFailedError
     */
    public static function assertNotFalse($condition, $message = '')
    {
        static::assertThat($condition, static::logicalNot(static::isFalse()), $message);
    }

    /**
     * Asserts that a variable is null.
     *
     * @param mixed  $actual
     * @param string $message
     */
    public static function assertNull($actual, $message = '')
    {
        static::assertThat($actual, static::isNull(), $message);
    }

    /**
     * Asserts that a variable is not null.
     *
     * @param mixed  $actual
     * @param string $message
     */
    public static function assertNotNull($actual, $message = '')
    {
        static::assertThat($actual, static::logicalNot(static::isNull()), $message);
    }

    /**
     * Asserts that a variable is finite.
     *
     * @param mixed  $actual
     * @param string $message
     */
    public static function assertFinite($actual, $message = '')
    {
        static::assertThat($actual, static::isFinite(), $message);
    }

    /**
     * Asserts that a variable is infinite.
     *
     * @param mixed  $actual
     * @param string $message
     */
    public static function assertInfinite($actual, $message = '')
    {
        static::assertThat($actual, static::isInfinite(), $message);
    }

    /**
     * Asserts that a variable is nan.
     *
     * @param mixed  $actual
     * @param string $message
     */
    public static function assertNan($actual, $message = '')
    {
        static::assertThat($actual, static::isNan(), $message);
    }

    /**
     * Asserts that a class has a specified attribute.
     *
     * @param string $attributeName
     * @param string $className
     * @param string $message
     */
    public static function assertClassHasAttribute($attributeName, $className, $message = '')
    {
        if (!is_string($attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name');
        }

        if (!is_string($className) || !class_exists($className)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'class name', $className);
        }

        $constraint = new PHPUnit_Framework_Constraint_ClassHasAttribute(
            $attributeName
        );

        static::assertThat($className, $constraint, $message);
    }

    /**
     * Asserts that a class does not have a specified attribute.
     *
     * @param string $attributeName
     * @param string $className
     * @param string $message
     */
    public static function assertClassNotHasAttribute($attributeName, $className, $message = '')
    {
        if (!is_string($attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name');
        }

        if (!is_string($className) || !class_exists($className)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'class name', $className);
        }

        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_ClassHasAttribute($attributeName)
        );

        static::assertThat($className, $constraint, $message);
    }

    /**
     * Asserts that a class has a specified static attribute.
     *
     * @param string $attributeName
     * @param string $className
     * @param string $message
     */
    public static function assertClassHasStaticAttribute($attributeName, $className, $message = '')
    {
        if (!is_string($attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name');
        }

        if (!is_string($className) || !class_exists($className)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'class name', $className);
        }

        $constraint = new PHPUnit_Framework_Constraint_ClassHasStaticAttribute(
            $attributeName
        );

        static::assertThat($className, $constraint, $message);
    }

    /**
     * Asserts that a class does not have a specified static attribute.
     *
     * @param string $attributeName
     * @param string $className
     * @param string $message
     */
    public static function assertClassNotHasStaticAttribute($attributeName, $className, $message = '')
    {
        if (!is_string($attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name');
        }

        if (!is_string($className) || !class_exists($className)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'class name', $className);
        }

        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_ClassHasStaticAttribute(
                $attributeName
            )
        );

        static::assertThat($className, $constraint, $message);
    }

    /**
     * Asserts that an object has a specified attribute.
     *
     * @param string $attributeName
     * @param object $object
     * @param string $message
     */
    public static function assertObjectHasAttribute($attributeName, $object, $message = '')
    {
        if (!is_string($attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name');
        }

        if (!is_object($object)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'object');
        }

        $constraint = new PHPUnit_Framework_Constraint_ObjectHasAttribute(
            $attributeName
        );

        static::assertThat($object, $constraint, $message);
    }

    /**
     * Asserts that an object does not have a specified attribute.
     *
     * @param string $attributeName
     * @param object $object
     * @param string $message
     */
    public static function assertObjectNotHasAttribute($attributeName, $object, $message = '')
    {
        if (!is_string($attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name');
        }

        if (!is_object($object)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'object');
        }

        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_ObjectHasAttribute($attributeName)
        );

        static::assertThat($object, $constraint, $message);
    }

    /**
     * Asserts that two variables have the same type and value.
     * Used on objects, it asserts that two variables reference
     * the same object.
     *
     * @param mixed  $expected
     * @param mixed  $actual
     * @param string $message
     */
    public static function assertSame($expected, $actual, $message = '')
    {
        if (is_bool($expected) && is_bool($actual)) {
            static::assertEquals($expected, $actual, $message);
        } else {
            $constraint = new PHPUnit_Framework_Constraint_IsIdentical(
                $expected
            );

            static::assertThat($actual, $constraint, $message);
        }
    }

    /**
     * Asserts that a variable and an attribute of an object have the same type
     * and value.
     *
     * @param mixed         $expected
     * @param string        $actualAttributeName
     * @param string|object $actualClassOrObject
     * @param string        $message
     */
    public static function assertAttributeSame($expected, $actualAttributeName, $actualClassOrObject, $message = '')
    {
        static::assertSame(
            $expected,
            static::readAttribute($actualClassOrObject, $actualAttributeName),
            $message
        );
    }

    /**
     * Asserts that two variables do not have the same type and value.
     * Used on objects, it asserts that two variables do not reference
     * the same object.
     *
     * @param mixed  $expected
     * @param mixed  $actual
     * @param string $message
     */
    public static function assertNotSame($expected, $actual, $message = '')
    {
        if (is_bool($expected) && is_bool($actual)) {
            static::assertNotEquals($expected, $actual, $message);
        } else {
            $constraint = new PHPUnit_Framework_Constraint_Not(
                new PHPUnit_Framework_Constraint_IsIdentical($expected)
            );

            static::assertThat($actual, $constraint, $message);
        }
    }

    /**
     * Asserts that a variable and an attribute of an object do not have the
     * same type and value.
     *
     * @param mixed         $expected
     * @param string        $actualAttributeName
     * @param string|object $actualClassOrObject
     * @param string        $message
     */
    public static function assertAttributeNotSame($expected, $actualAttributeName, $actualClassOrObject, $message = '')
    {
        static::assertNotSame(
            $expected,
            static::readAttribute($actualClassOrObject, $actualAttributeName),
            $message
        );
    }

    /**
     * Asserts that a variable is of a given type.
     *
     * @param string $expected
     * @param mixed  $actual
     * @param string $message
     */
    public static function assertInstanceOf($expected, $actual, $message = '')
    {
        if (!(is_string($expected) && (class_exists($expected) || interface_exists($expected)))) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'class or interface name');
        }

        $constraint = new PHPUnit_Framework_Constraint_IsInstanceOf(
            $expected
        );

        static::assertThat($actual, $constraint, $message);
    }

    /**
     * Asserts that an attribute is of a given type.
     *
     * @param string        $expected
     * @param string        $attributeName
     * @param string|object $classOrObject
     * @param string        $message
     */
    public static function assertAttributeInstanceOf($expected, $attributeName, $classOrObject, $message = '')
    {
        static::assertInstanceOf(
            $expected,
            static::readAttribute($classOrObject, $attributeName),
            $message
        );
    }

    /**
     * Asserts that a variable is not of a given type.
     *
     * @param string $expected
     * @param mixed  $actual
     * @param string $message
     */
    public static function assertNotInstanceOf($expected, $actual, $message = '')
    {
        if (!(is_string($expected) && (class_exists($expected) || interface_exists($expected)))) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'class or interface name');
        }

        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_IsInstanceOf($expected)
        );

        static::assertThat($actual, $constraint, $message);
    }

    /**
     * Asserts that an attribute is of a given type.
     *
     * @param string        $expected
     * @param string        $attributeName
     * @param string|object $classOrObject
     * @param string        $message
     */
    public static function assertAttributeNotInstanceOf($expected, $attributeName, $classOrObject, $message = '')
    {
        static::assertNotInstanceOf(
            $expected,
            static::readAttribute($classOrObject, $attributeName),
            $message
        );
    }

    /**
     * Asserts that a variable is of a given type.
     *
     * @param string $expected
     * @param mixed  $actual
     * @param string $message
     */
    public static function assertInternalType($expected, $actual, $message = '')
    {
        if (!is_string($expected)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_IsType(
            $expected
        );

        static::assertThat($actual, $constraint, $message);
    }

    /**
     * Asserts that an attribute is of a given type.
     *
     * @param string        $expected
     * @param string        $attributeName
     * @param string|object $classOrObject
     * @param string        $message
     */
    public static function assertAttributeInternalType($expected, $attributeName, $classOrObject, $message = '')
    {
        static::assertInternalType(
            $expected,
            static::readAttribute($classOrObject, $attributeName),
            $message
        );
    }

    /**
     * Asserts that a variable is not of a given type.
     *
     * @param string $expected
     * @param mixed  $actual
     * @param string $message
     */
    public static function assertNotInternalType($expected, $actual, $message = '')
    {
        if (!is_string($expected)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_IsType($expected)
        );

        static::assertThat($actual, $constraint, $message);
    }

    /**
     * Asserts that an attribute is of a given type.
     *
     * @param string        $expected
     * @param string        $attributeName
     * @param string|object $classOrObject
     * @param string        $message
     */
    public static function assertAttributeNotInternalType($expected, $attributeName, $classOrObject, $message = '')
    {
        static::assertNotInternalType(
            $expected,
            static::readAttribute($classOrObject, $attributeName),
            $message
        );
    }

    /**
     * Asserts that a string matches a given regular expression.
     *
     * @param string $pattern
     * @param string $string
     * @param string $message
     */
    public static function assertRegExp($pattern, $string, $message = '')
    {
        if (!is_string($pattern)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!is_string($string)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_PCREMatch($pattern);

        static::assertThat($string, $constraint, $message);
    }

    /**
     * Asserts that a string does not match a given regular expression.
     *
     * @param string $pattern
     * @param string $string
     * @param string $message
     */
    public static function assertNotRegExp($pattern, $string, $message = '')
    {
        if (!is_string($pattern)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!is_string($string)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_PCREMatch($pattern)
        );

        static::assertThat($string, $constraint, $message);
    }

    /**
     * Assert that the size of two arrays (or `Countable` or `Traversable` objects)
     * is the same.
     *
     * @param array|Countable|Traversable $expected
     * @param array|Countable|Traversable $actual
     * @param string                      $message
     */
    public static function assertSameSize($expected, $actual, $message = '')
    {
        if (!$expected instanceof Countable &&
            !$expected instanceof Traversable &&
            !is_array($expected)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'countable or traversable');
        }

        if (!$actual instanceof Countable &&
            !$actual instanceof Traversable &&
            !is_array($actual)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'countable or traversable');
        }

        static::assertThat(
            $actual,
            new PHPUnit_Framework_Constraint_SameSize($expected),
            $message
        );
    }

    /**
     * Assert that the size of two arrays (or `Countable` or `Traversable` objects)
     * is not the same.
     *
     * @param array|Countable|Traversable $expected
     * @param array|Countable|Traversable $actual
     * @param string                      $message
     */
    public static function assertNotSameSize($expected, $actual, $message = '')
    {
        if (!$expected instanceof Countable &&
            !$expected instanceof Traversable &&
            !is_array($expected)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'countable or traversable');
        }

        if (!$actual instanceof Countable &&
            !$actual instanceof Traversable &&
            !is_array($actual)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'countable or traversable');
        }

        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_SameSize($expected)
        );

        static::assertThat($actual, $constraint, $message);
    }

    /**
     * Asserts that a string matches a given format string.
     *
     * @param string $format
     * @param string $string
     * @param string $message
     */
    public static function assertStringMatchesFormat($format, $string, $message = '')
    {
        if (!is_string($format)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!is_string($string)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_StringMatches($format);

        static::assertThat($string, $constraint, $message);
    }

    /**
     * Asserts that a string does not match a given format string.
     *
     * @param string $format
     * @param string $string
     * @param string $message
     */
    public static function assertStringNotMatchesFormat($format, $string, $message = '')
    {
        if (!is_string($format)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!is_string($string)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_StringMatches($format)
        );

        static::assertThat($string, $constraint, $message);
    }

    /**
     * Asserts that a string matches a given format file.
     *
     * @param string $formatFile
     * @param string $string
     * @param string $message
     */
    public static function assertStringMatchesFormatFile($formatFile, $string, $message = '')
    {
        static::assertFileExists($formatFile, $message);

        if (!is_string($string)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_StringMatches(
            file_get_contents($formatFile)
        );

        static::assertThat($string, $constraint, $message);
    }

    /**
     * Asserts that a string does not match a given format string.
     *
     * @param string $formatFile
     * @param string $string
     * @param string $message
     */
    public static function assertStringNotMatchesFormatFile($formatFile, $string, $message = '')
    {
        static::assertFileExists($formatFile, $message);

        if (!is_string($string)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_StringMatches(
                file_get_contents($formatFile)
            )
        );

        static::assertThat($string, $constraint, $message);
    }

    /**
     * Asserts that a string starts with a given prefix.
     *
     * @param string $prefix
     * @param string $string
     * @param string $message
     */
    public static function assertStringStartsWith($prefix, $string, $message = '')
    {
        if (!is_string($prefix)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!is_string($string)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_StringStartsWith(
            $prefix
        );

        static::assertThat($string, $constraint, $message);
    }

    /**
     * Asserts that a string starts not with a given prefix.
     *
     * @param string $prefix
     * @param string $string
     * @param string $message
     */
    public static function assertStringStartsNotWith($prefix, $string, $message = '')
    {
        if (!is_string($prefix)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!is_string($string)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_StringStartsWith($prefix)
        );

        static::assertThat($string, $constraint, $message);
    }

    /**
     * Asserts that a string ends with a given suffix.
     *
     * @param string $suffix
     * @param string $string
     * @param string $message
     */
    public static function assertStringEndsWith($suffix, $string, $message = '')
    {
        if (!is_string($suffix)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!is_string($string)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_StringEndsWith($suffix);

        static::assertThat($string, $constraint, $message);
    }

    /**
     * Asserts that a string ends not with a given suffix.
     *
     * @param string $suffix
     * @param string $string
     * @param string $message
     */
    public static function assertStringEndsNotWith($suffix, $string, $message = '')
    {
        if (!is_string($suffix)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!is_string($string)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
        }

        $constraint = new PHPUnit_Framework_Constraint_Not(
            new PHPUnit_Framework_Constraint_StringEndsWith($suffix)
        );

        static::assertThat($string, $constraint, $message);
    }

    /**
     * Asserts that two XML files are equal.
     *
     * @param string $expectedFile
     * @param string $actualFile
     * @param string $message
     */
    public static function assertXmlFileEqualsXmlFile($expectedFile, $actualFile, $message = '')
    {
        $expected = PHPUnit_Util_XML::loadFile($expectedFile);
        $actual   = PHPUnit_Util_XML::loadFile($actualFile);

        static::assertEquals($expected, $actual, $message);
    }

    /**
     * Asserts that two XML files are not equal.
     *
     * @param string $expectedFile
     * @param string $actualFile
     * @param string $message
     */
    public static function assertXmlFileNotEqualsXmlFile($expectedFile, $actualFile, $message = '')
    {
        $expected = PHPUnit_Util_XML::loadFile($expectedFile);
        $actual   = PHPUnit_Util_XML::loadFile($actualFile);

        static::assertNotEquals($expected, $actual, $message);
    }

    /**
     * Asserts that two XML documents are equal.
     *
     * @param string $expectedFile
     * @param string $actualXml
     * @param string $message
     */
    public static function assertXmlStringEqualsXmlFile($expectedFile, $actualXml, $message = '')
    {
        $expected = PHPUnit_Util_XML::loadFile($expectedFile);
        $actual   = PHPUnit_Util_XML::load($actualXml);

        static::assertEquals($expected, $actual, $message);
    }

    /**
     * Asserts that two XML documents are not equal.
     *
     * @param string $expectedFile
     * @param string $actualXml
     * @param string $message
     */
    public static function assertXmlStringNotEqualsXmlFile($expectedFile, $actualXml, $message = '')
    {
        $expected = PHPUnit_Util_XML::loadFile($expectedFile);
        $actual   = PHPUnit_Util_XML::load($actualXml);

        static::assertNotEquals($expected, $actual, $message);
    }

    /**
     * Asserts that two XML documents are equal.
     *
     * @param string $expectedXml
     * @param string $actualXml
     * @param string $message
     */
    public static function assertXmlStringEqualsXmlString($expectedXml, $actualXml, $message = '')
    {
        $expected = PHPUnit_Util_XML::load($expectedXml);
        $actual   = PHPUnit_Util_XML::load($actualXml);

        static::assertEquals($expected, $actual, $message);
    }

    /**
     * Asserts that two XML documents are not equal.
     *
     * @param string $expectedXml
     * @param string $actualXml
     * @param string $message
     */
    public static function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, $message = '')
    {
        $expected = PHPUnit_Util_XML::load($expectedXml);
        $actual   = PHPUnit_Util_XML::load($actualXml);

        static::assertNotEquals($expected, $actual, $message);
    }

    /**
     * Asserts that a hierarchy of DOMElements matches.
     *
     * @param DOMElement $expectedElement
     * @param DOMElement $actualElement
     * @param bool       $checkAttributes
     * @param string     $message
     */
    public static function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, $checkAttributes = false, $message = '')
    {
        $tmp             = new DOMDocument;
        $expectedElement = $tmp->importNode($expectedElement, true);

        $tmp           = new DOMDocument;
        $actualElement = $tmp->importNode($actualElement, true);

        unset($tmp);

        static::assertEquals(
            $expectedElement->tagName,
            $actualElement->tagName,
            $message
        );

        if ($checkAttributes) {
            static::assertEquals(
                $expectedElement->attributes->length,
                $actualElement->attributes->length,
                sprintf(
                    '%s%sNumber of attributes on node "%s" does not match',
                    $message,
                    !empty($message) ? "\n" : '',
                    $expectedElement->tagName
                )
            );

            for ($i = 0; $i < $expectedElement->attributes->length; $i++) {
                $expectedAttribute = $expectedElement->attributes->item($i);
                $actualAttribute   = $actualElement->attributes->getNamedItem(
                    $expectedAttribute->name
                );

                if (!$actualAttribute) {
                    static::fail(
                        sprintf(
                            '%s%sCould not find attribute "%s" on node "%s"',
                            $message,
                            !empty($message) ? "\n" : '',
                            $expectedAttribute->name,
                            $expectedElement->tagName
                        )
                    );
                }
            }
        }

        PHPUnit_Util_XML::removeCharacterDataNodes($expectedElement);
        PHPUnit_Util_XML::removeCharacterDataNodes($actualElement);

        static::assertEquals(
            $expectedElement->childNodes->length,
            $actualElement->childNodes->length,
            sprintf(
                '%s%sNumber of child nodes of "%s" differs',
                $message,
                !empty($message) ? "\n" : '',
                $expectedElement->tagName
            )
        );

        for ($i = 0; $i < $expectedElement->childNodes->length; $i++) {
            static::assertEqualXMLStructure(
                $expectedElement->childNodes->item($i),
                $actualElement->childNodes->item($i),
                $checkAttributes,
                $message
            );
        }
    }

    /**
     * Evaluates a PHPUnit_Framework_Constraint matcher object.
     *
     * @param mixed                        $value
     * @param PHPUnit_Framework_Constraint $constraint
     * @param string                       $message
     */
    public static function assertThat($value, PHPUnit_Framework_Constraint $constraint, $message = '')
    {
        self::$count += count($constraint);

        $constraint->evaluate($value, $message);
    }

    /**
     * Asserts that a string is a valid JSON string.
     *
     * @param string $actualJson
     * @param string $message
     */
    public static function assertJson($actualJson, $message = '')
    {
        if (!is_string($actualJson)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        static::assertThat($actualJson, static::isJson(), $message);
    }

    /**
     * Asserts that two given JSON encoded objects or arrays are equal.
     *
     * @param string $expectedJson
     * @param string $actualJson
     * @param string $message
     */
    public static function assertJsonStringEqualsJsonString($expectedJson, $actualJson, $message = '')
    {
        static::assertJson($expectedJson, $message);
        static::assertJson($actualJson, $message);

        $expected = json_decode($expectedJson);
        $actual   = json_decode($actualJson);

        static::assertEquals($expected, $actual, $message);
    }

    /**
     * Asserts that two given JSON encoded objects or arrays are not equal.
     *
     * @param string $expectedJson
     * @param string $actualJson
     * @param string $message
     */
    public static function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, $message = '')
    {
        static::assertJson($expectedJson, $message);
        static::assertJson($actualJson, $message);

        $expected = json_decode($expectedJson);
        $actual   = json_decode($actualJson);

        static::assertNotEquals($expected, $actual, $message);
    }

    /**
     * Asserts that the generated JSON encoded object and the content of the given file are equal.
     *
     * @param string $expectedFile
     * @param string $actualJson
     * @param string $message
     */
    public static function assertJsonStringEqualsJsonFile($expectedFile, $actualJson, $message = '')
    {
        static::assertFileExists($expectedFile, $message);
        $expectedJson = file_get_contents($expectedFile);

        static::assertJson($expectedJson, $message);
        static::assertJson($actualJson, $message);

        // call constraint
        $constraint = new PHPUnit_Framework_Constraint_JsonMatches(
            $expectedJson
        );

        static::assertThat($actualJson, $constraint, $message);
    }

    /**
     * Asserts that the generated JSON encoded object and the content of the given file are not equal.
     *
     * @param string $expectedFile
     * @param string $actualJson
     * @param string $message
     */
    public static function assertJsonStringNotEqualsJsonFile($expectedFile, $actualJson, $message = '')
    {
        static::assertFileExists($expectedFile, $message);
        $expectedJson = file_get_contents($expectedFile);

        static::assertJson($expectedJson, $message);
        static::assertJson($actualJson, $message);

        // call constraint
        $constraint = new PHPUnit_Framework_Constraint_JsonMatches(
            $expectedJson
        );

        static::assertThat($actualJson, new PHPUnit_Framework_Constraint_Not($constraint), $message);
    }

    /**
     * Asserts that two JSON files are equal.
     *
     * @param string $expectedFile
     * @param string $actualFile
     * @param string $message
     */
    public static function assertJsonFileEqualsJsonFile($expectedFile, $actualFile, $message = '')
    {
        static::assertFileExists($expectedFile, $message);
        static::assertFileExists($actualFile, $message);

        $actualJson   = file_get_contents($actualFile);
        $expectedJson = file_get_contents($expectedFile);

        static::assertJson($expectedJson, $message);
        static::assertJson($actualJson, $message);

        // call constraint
        $constraintExpected = new PHPUnit_Framework_Constraint_JsonMatches(
            $expectedJson
        );

        $constraintActual = new PHPUnit_Framework_Constraint_JsonMatches($actualJson);

        static::assertThat($expectedJson, $constraintActual, $message);
        static::assertThat($actualJson, $constraintExpected, $message);
    }

    /**
     * Asserts that two JSON files are not equal.
     *
     * @param string $expectedFile
     * @param string $actualFile
     * @param string $message
     */
    public static function assertJsonFileNotEqualsJsonFile($expectedFile, $actualFile, $message = '')
    {
        static::assertFileExists($expectedFile, $message);
        static::assertFileExists($actualFile, $message);

        $actualJson   = file_get_contents($actualFile);
        $expectedJson = file_get_contents($expectedFile);

        static::assertJson($expectedJson, $message);
        static::assertJson($actualJson, $message);

        // call constraint
        $constraintExpected = new PHPUnit_Framework_Constraint_JsonMatches(
            $expectedJson
        );

        $constraintActual = new PHPUnit_Framework_Constraint_JsonMatches($actualJson);

        static::assertThat($expectedJson, new PHPUnit_Framework_Constraint_Not($constraintActual), $message);
        static::assertThat($actualJson, new PHPUnit_Framework_Constraint_Not($constraintExpected), $message);
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_And matcher object.
     *
     * @return PHPUnit_Framework_Constraint_And
     */
    public static function logicalAnd()
    {
        $constraints = func_get_args();

        $constraint = new PHPUnit_Framework_Constraint_And;
        $constraint->setConstraints($constraints);

        return $constraint;
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_Or matcher object.
     *
     * @return PHPUnit_Framework_Constraint_Or
     */
    public static function logicalOr()
    {
        $constraints = func_get_args();

        $constraint = new PHPUnit_Framework_Constraint_Or;
        $constraint->setConstraints($constraints);

        return $constraint;
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_Not matcher object.
     *
     * @param PHPUnit_Framework_Constraint $constraint
     *
     * @return PHPUnit_Framework_Constraint_Not
     */
    public static function logicalNot(PHPUnit_Framework_Constraint $constraint)
    {
        return new PHPUnit_Framework_Constraint_Not($constraint);
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_Xor matcher object.
     *
     * @return PHPUnit_Framework_Constraint_Xor
     */
    public static function logicalXor()
    {
        $constraints = func_get_args();

        $constraint = new PHPUnit_Framework_Constraint_Xor;
        $constraint->setConstraints($constraints);

        return $constraint;
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_IsAnything matcher object.
     *
     * @return PHPUnit_Framework_Constraint_IsAnything
     */
    public static function anything()
    {
        return new PHPUnit_Framework_Constraint_IsAnything;
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_IsTrue matcher object.
     *
     * @return PHPUnit_Framework_Constraint_IsTrue
     */
    public static function isTrue()
    {
        return new PHPUnit_Framework_Constraint_IsTrue;
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_Callback matcher object.
     *
     * @param callable $callback
     *
     * @return PHPUnit_Framework_Constraint_Callback
     */
    public static function callback($callback)
    {
        return new PHPUnit_Framework_Constraint_Callback($callback);
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_IsFalse matcher object.
     *
     * @return PHPUnit_Framework_Constraint_IsFalse
     */
    public static function isFalse()
    {
        return new PHPUnit_Framework_Constraint_IsFalse;
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_IsJson matcher object.
     *
     * @return PHPUnit_Framework_Constraint_IsJson
     */
    public static function isJson()
    {
        return new PHPUnit_Framework_Constraint_IsJson;
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_IsNull matcher object.
     *
     * @return PHPUnit_Framework_Constraint_IsNull
     */
    public static function isNull()
    {
        return new PHPUnit_Framework_Constraint_IsNull;
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_IsFinite matcher object.
     *
     * @return PHPUnit_Framework_Constraint_IsFinite
     */
    public static function isFinite()
    {
        return new PHPUnit_Framework_Constraint_IsFinite;
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_IsInfinite matcher object.
     *
     * @return PHPUnit_Framework_Constraint_IsInfinite
     */
    public static function isInfinite()
    {
        return new PHPUnit_Framework_Constraint_IsInfinite;
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_IsNan matcher object.
     *
     * @return PHPUnit_Framework_Constraint_IsNan
     */
    public static function isNan()
    {
        return new PHPUnit_Framework_Constraint_IsNan;
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_Attribute matcher object.
     *
     * @param PHPUnit_Framework_Constraint $constraint
     * @param string                       $attributeName
     *
     * @return PHPUnit_Framework_Constraint_Attribute
     */
    public static function attribute(PHPUnit_Framework_Constraint $constraint, $attributeName)
    {
        return new PHPUnit_Framework_Constraint_Attribute(
            $constraint,
            $attributeName
        );
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_TraversableContains matcher
     * object.
     *
     * @param mixed $value
     * @param bool  $checkForObjectIdentity
     * @param bool  $checkForNonObjectIdentity
     *
     * @return PHPUnit_Framework_Constraint_TraversableContains
     */
    public static function contains($value, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
    {
        return new PHPUnit_Framework_Constraint_TraversableContains($value, $checkForObjectIdentity, $checkForNonObjectIdentity);
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_TraversableContainsOnly matcher
     * object.
     *
     * @param string $type
     *
     * @return PHPUnit_Framework_Constraint_TraversableContainsOnly
     */
    public static function containsOnly($type)
    {
        return new PHPUnit_Framework_Constraint_TraversableContainsOnly($type);
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_TraversableContainsOnly matcher
     * object.
     *
     * @param string $classname
     *
     * @return PHPUnit_Framework_Constraint_TraversableContainsOnly
     */
    public static function containsOnlyInstancesOf($classname)
    {
        return new PHPUnit_Framework_Constraint_TraversableContainsOnly($classname, false);
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_ArrayHasKey matcher object.
     *
     * @param mixed $key
     *
     * @return PHPUnit_Framework_Constraint_ArrayHasKey
     */
    public static function arrayHasKey($key)
    {
        return new PHPUnit_Framework_Constraint_ArrayHasKey($key);
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_IsEqual matcher object.
     *
     * @param mixed $value
     * @param float $delta
     * @param int   $maxDepth
     * @param bool  $canonicalize
     * @param bool  $ignoreCase
     *
     * @return PHPUnit_Framework_Constraint_IsEqual
     */
    public static function equalTo($value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
    {
        return new PHPUnit_Framework_Constraint_IsEqual(
            $value,
            $delta,
            $maxDepth,
            $canonicalize,
            $ignoreCase
        );
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_IsEqual matcher object
     * that is wrapped in a PHPUnit_Framework_Constraint_Attribute matcher
     * object.
     *
     * @param string $attributeName
     * @param mixed  $value
     * @param float  $delta
     * @param int    $maxDepth
     * @param bool   $canonicalize
     * @param bool   $ignoreCase
     *
     * @return PHPUnit_Framework_Constraint_Attribute
     */
    public static function attributeEqualTo($attributeName, $value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
    {
        return static::attribute(
            static::equalTo(
                $value,
                $delta,
                $maxDepth,
                $canonicalize,
                $ignoreCase
            ),
            $attributeName
        );
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_IsEmpty matcher object.
     *
     * @return PHPUnit_Framework_Constraint_IsEmpty
     */
    public static function isEmpty()
    {
        return new PHPUnit_Framework_Constraint_IsEmpty;
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_IsWritable matcher object.
     *
     * @return PHPUnit_Framework_Constraint_IsWritable
     */
    public static function isWritable()
    {
        return new PHPUnit_Framework_Constraint_IsWritable;
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_IsReadable matcher object.
     *
     * @return PHPUnit_Framework_Constraint_IsReadable
     */
    public static function isReadable()
    {
        return new PHPUnit_Framework_Constraint_IsReadable;
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_DirectoryExists matcher object.
     *
     * @return PHPUnit_Framework_Constraint_DirectoryExists
     */
    public static function directoryExists()
    {
        return new PHPUnit_Framework_Constraint_DirectoryExists;
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_FileExists matcher object.
     *
     * @return PHPUnit_Framework_Constraint_FileExists
     */
    public static function fileExists()
    {
        return new PHPUnit_Framework_Constraint_FileExists;
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_GreaterThan matcher object.
     *
     * @param mixed $value
     *
     * @return PHPUnit_Framework_Constraint_GreaterThan
     */
    public static function greaterThan($value)
    {
        return new PHPUnit_Framework_Constraint_GreaterThan($value);
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_Or matcher object that wraps
     * a PHPUnit_Framework_Constraint_IsEqual and a
     * PHPUnit_Framework_Constraint_GreaterThan matcher object.
     *
     * @param mixed $value
     *
     * @return PHPUnit_Framework_Constraint_Or
     */
    public static function greaterThanOrEqual($value)
    {
        return static::logicalOr(
            new PHPUnit_Framework_Constraint_IsEqual($value),
            new PHPUnit_Framework_Constraint_GreaterThan($value)
        );
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_ClassHasAttribute matcher object.
     *
     * @param string $attributeName
     *
     * @return PHPUnit_Framework_Constraint_ClassHasAttribute
     */
    public static function classHasAttribute($attributeName)
    {
        return new PHPUnit_Framework_Constraint_ClassHasAttribute(
            $attributeName
        );
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_ClassHasStaticAttribute matcher
     * object.
     *
     * @param string $attributeName
     *
     * @return PHPUnit_Framework_Constraint_ClassHasStaticAttribute
     */
    public static function classHasStaticAttribute($attributeName)
    {
        return new PHPUnit_Framework_Constraint_ClassHasStaticAttribute(
            $attributeName
        );
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_ObjectHasAttribute matcher object.
     *
     * @param string $attributeName
     *
     * @return PHPUnit_Framework_Constraint_ObjectHasAttribute
     */
    public static function objectHasAttribute($attributeName)
    {
        return new PHPUnit_Framework_Constraint_ObjectHasAttribute(
            $attributeName
        );
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_IsIdentical matcher object.
     *
     * @param mixed $value
     *
     * @return PHPUnit_Framework_Constraint_IsIdentical
     */
    public static function identicalTo($value)
    {
        return new PHPUnit_Framework_Constraint_IsIdentical($value);
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_IsInstanceOf matcher object.
     *
     * @param string $className
     *
     * @return PHPUnit_Framework_Constraint_IsInstanceOf
     */
    public static function isInstanceOf($className)
    {
        return new PHPUnit_Framework_Constraint_IsInstanceOf($className);
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_IsType matcher object.
     *
     * @param string $type
     *
     * @return PHPUnit_Framework_Constraint_IsType
     */
    public static function isType($type)
    {
        return new PHPUnit_Framework_Constraint_IsType($type);
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_LessThan matcher object.
     *
     * @param mixed $value
     *
     * @return PHPUnit_Framework_Constraint_LessThan
     */
    public static function lessThan($value)
    {
        return new PHPUnit_Framework_Constraint_LessThan($value);
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_Or matcher object that wraps
     * a PHPUnit_Framework_Constraint_IsEqual and a
     * PHPUnit_Framework_Constraint_LessThan matcher object.
     *
     * @param mixed $value
     *
     * @return PHPUnit_Framework_Constraint_Or
     */
    public static function lessThanOrEqual($value)
    {
        return static::logicalOr(
            new PHPUnit_Framework_Constraint_IsEqual($value),
            new PHPUnit_Framework_Constraint_LessThan($value)
        );
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_PCREMatch matcher object.
     *
     * @param string $pattern
     *
     * @return PHPUnit_Framework_Constraint_PCREMatch
     */
    public static function matchesRegularExpression($pattern)
    {
        return new PHPUnit_Framework_Constraint_PCREMatch($pattern);
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_StringMatches matcher object.
     *
     * @param string $string
     *
     * @return PHPUnit_Framework_Constraint_StringMatches
     */
    public static function matches($string)
    {
        return new PHPUnit_Framework_Constraint_StringMatches($string);
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_StringStartsWith matcher object.
     *
     * @param mixed $prefix
     *
     * @return PHPUnit_Framework_Constraint_StringStartsWith
     */
    public static function stringStartsWith($prefix)
    {
        return new PHPUnit_Framework_Constraint_StringStartsWith($prefix);
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_StringContains matcher object.
     *
     * @param string $string
     * @param bool   $case
     *
     * @return PHPUnit_Framework_Constraint_StringContains
     */
    public static function stringContains($string, $case = true)
    {
        return new PHPUnit_Framework_Constraint_StringContains($string, $case);
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_StringEndsWith matcher object.
     *
     * @param mixed $suffix
     *
     * @return PHPUnit_Framework_Constraint_StringEndsWith
     */
    public static function stringEndsWith($suffix)
    {
        return new PHPUnit_Framework_Constraint_StringEndsWith($suffix);
    }

    /**
     * Returns a PHPUnit_Framework_Constraint_Count matcher object.
     *
     * @param int $count
     *
     * @return PHPUnit_Framework_Constraint_Count
     */
    public static function countOf($count)
    {
        return new PHPUnit_Framework_Constraint_Count($count);
    }
    /**
     * Fails a test with the given message.
     *
     * @param string $message
     *
     * @throws PHPUnit_Framework_AssertionFailedError
     */
    public static function fail($message = '')
    {
        throw new PHPUnit_Framework_AssertionFailedError($message);
    }

    /**
     * Returns the value of an attribute of a class or an object.
     * This also works for attributes that are declared protected or private.
     *
     * @param string|object $classOrObject
     * @param string        $attributeName
     *
     * @return mixed
     *
     * @throws PHPUnit_Framework_Exception
     */
    public static function readAttribute($classOrObject, $attributeName)
    {
        if (!is_string($attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
        }

        if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'valid attribute name');
        }

        if (is_string($classOrObject)) {
            if (!class_exists($classOrObject)) {
                throw PHPUnit_Util_InvalidArgumentHelper::factory(
                    1,
                    'class name'
                );
            }

            return static::getStaticAttribute(
                $classOrObject,
                $attributeName
            );
        } elseif (is_object($classOrObject)) {
            return static::getObjectAttribute(
                $classOrObject,
                $attributeName
            );
        } else {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(
                1,
                'class name or object'
            );
        }
    }

    /**
     * Returns the value of a static attribute.
     * This also works for attributes that are declared protected or private.
     *
     * @param string $className
     * @param string $attributeName
     *
     * @return mixed
     *
     * @throws PHPUnit_Framework_Exception
     */
    public static function getStaticAttribute($className, $attributeName)
    {
        if (!is_string($className)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!class_exists($className)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'class name');
        }

        if (!is_string($attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
        }

        if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'valid attribute name');
        }

        $class = new ReflectionClass($className);

        while ($class) {
            $attributes = $class->getStaticProperties();

            if (array_key_exists($attributeName, $attributes)) {
                return $attributes[$attributeName];
            }

            $class = $class->getParentClass();
        }

        throw new PHPUnit_Framework_Exception(
            sprintf(
                'Attribute "%s" not found in class.',
                $attributeName
            )
        );
    }

    /**
     * Returns the value of an object's attribute.
     * This also works for attributes that are declared protected or private.
     *
     * @param object $object
     * @param string $attributeName
     *
     * @return mixed
     *
     * @throws PHPUnit_Framework_Exception
     */
    public static function getObjectAttribute($object, $attributeName)
    {
        if (!is_object($object)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'object');
        }

        if (!is_string($attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
        }

        if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'valid attribute name');
        }

        try {
            $attribute = new ReflectionProperty($object, $attributeName);
        } catch (ReflectionException $e) {
            $reflector = new ReflectionObject($object);

            while ($reflector = $reflector->getParentClass()) {
                try {
                    $attribute = $reflector->getProperty($attributeName);
                    break;
                } catch (ReflectionException $e) {
                }
            }
        }

        if (isset($attribute)) {
            if (!$attribute || $attribute->isPublic()) {
                return $object->$attributeName;
            }

            $attribute->setAccessible(true);
            $value = $attribute->getValue($object);
            $attribute->setAccessible(false);

            return $value;
        }

        throw new PHPUnit_Framework_Exception(
            sprintf(
                'Attribute "%s" not found in object.',
                $attributeName
            )
        );
    }

    /**
     * Mark the test as incomplete.
     *
     * @param string $message
     *
     * @throws PHPUnit_Framework_IncompleteTestError
     */
    public static function markTestIncomplete($message = '')
    {
        throw new PHPUnit_Framework_IncompleteTestError($message);
    }

    /**
     * Mark the test as skipped.
     *
     * @param string $message
     *
     * @throws PHPUnit_Framework_SkippedTestError
     */
    public static function markTestSkipped($message = '')
    {
        throw new PHPUnit_Framework_SkippedTestError($message);
    }

    /**
     * Return the current assertion count.
     *
     * @return int
     */
    public static function getCount()
    {
        return self::$count;
    }

    /**
     * Reset the assertion counter.
     */
    public static function resetCount()
    {
        self::$count = 0;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Thrown when an assertion failed.
 */
class PHPUnit_Framework_AssertionFailedError extends PHPUnit_Framework_Exception implements PHPUnit_Framework_SelfDescribing
{
    /**
     * Wrapper for getMessage() which is declared as final.
     *
     * @return string
     */
    public function toString()
    {
        return $this->getMessage();
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * An empty Listener that can be extended to implement TestListener
 * with just a few lines of code.
 *
 * @see PHPUnit_Framework_TestListener for documentation on the API methods.
 */
abstract class PHPUnit_Framework_BaseTestListener implements PHPUnit_Framework_TestListener
{
    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
    }

    public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
    {
    }

    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
    {
    }

    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
    }

    public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
    }

    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
    }

    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
    }

    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
    }

    public function startTest(PHPUnit_Framework_Test $test)
    {
    }

    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Framework_CodeCoverageException extends PHPUnit_Framework_Exception
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Logical AND.
 */
class PHPUnit_Framework_Constraint_And extends PHPUnit_Framework_Constraint
{
    /**
     * @var PHPUnit_Framework_Constraint[]
     */
    protected $constraints = [];

    /**
     * @var PHPUnit_Framework_Constraint
     */
    protected $lastConstraint = null;

    /**
     * @param PHPUnit_Framework_Constraint[] $constraints
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function setConstraints(array $constraints)
    {
        $this->constraints = [];

        foreach ($constraints as $constraint) {
            if (!($constraint instanceof PHPUnit_Framework_Constraint)) {
                throw new PHPUnit_Framework_Exception(
                    'All parameters to ' . __CLASS__ .
                    ' must be a constraint object.'
                );
            }

            $this->constraints[] = $constraint;
        }
    }

    /**
     * Evaluates the constraint for parameter $other
     *
     * If $returnResult is set to false (the default), an exception is thrown
     * in case of a failure. null is returned otherwise.
     *
     * If $returnResult is true, the result of the evaluation is returned as
     * a boolean value instead: true in case of success, false in case of a
     * failure.
     *
     * @param mixed  $other        Value or object to evaluate.
     * @param string $description  Additional information about the test
     * @param bool   $returnResult Whether to return a result or throw an exception
     *
     * @return mixed
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function evaluate($other, $description = '', $returnResult = false)
    {
        $success    = true;
        $constraint = null;

        foreach ($this->constraints as $constraint) {
            if (!$constraint->evaluate($other, $description, true)) {
                $success = false;
                break;
            }
        }

        if ($returnResult) {
            return $success;
        }

        if (!$success) {
            $this->fail($other, $description);
        }
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        $text = '';

        foreach ($this->constraints as $key => $constraint) {
            if ($key > 0) {
                $text .= ' and ';
            }

            $text .= $constraint->toString();
        }

        return $text;
    }

    /**
     * Counts the number of constraint elements.
     *
     * @return int
     */
    public function count()
    {
        $count = 0;

        foreach ($this->constraints as $constraint) {
            $count += count($constraint);
        }

        return $count;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that asserts that the array it is evaluated for has a given key.
 *
 * Uses array_key_exists() to check if the key is found in the input array, if
 * not found the evaluation fails.
 *
 * The array key is passed in the constructor.
 */
class PHPUnit_Framework_Constraint_ArrayHasKey extends PHPUnit_Framework_Constraint
{
    /**
     * @var int|string
     */
    protected $key;

    /**
     * @param int|string $key
     */
    public function __construct($key)
    {
        parent::__construct();
        $this->key = $key;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        if (is_array($other)) {
            return array_key_exists($this->key, $other);
        }

        if ($other instanceof ArrayAccess) {
            return $other->offsetExists($this->key);
        }

        return false;
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'has the key ' . $this->exporter->export($this->key);
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        return 'an array ' . $this->toString();
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that asserts that the array it is evaluated for has a specified subset.
 *
 * Uses array_replace_recursive() to check if a key value subset is part of the
 * subject array.
 */
class PHPUnit_Framework_Constraint_ArraySubset extends PHPUnit_Framework_Constraint
{
    /**
     * @var array|Traversable
     */
    protected $subset;

    /**
     * @var bool
     */
    protected $strict;

    /**
     * @param array|Traversable $subset
     * @param bool              $strict Check for object identity
     */
    public function __construct($subset, $strict = false)
    {
        parent::__construct();
        $this->strict = $strict;
        $this->subset = $subset;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param array|Traversable $other Array or Traversable object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        //type cast $other & $this->subset as an array to allow
        //support in standard array functions.
        $other        = $this->toArray($other);
        $this->subset = $this->toArray($this->subset);

        $patched = array_replace_recursive($other, $this->subset);

        if ($this->strict) {
            return $other === $patched;
        } else {
            return $other == $patched;
        }
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'has the subset ' . $this->exporter->export($this->subset);
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        return 'an array ' . $this->toString();
    }

    /**
     * @param array|Traversable $other
     *
     * @return array
     */
    private function toArray($other)
    {
        if (is_array($other)) {
            return $other;
        } elseif ($other instanceof ArrayObject) {
            return $other->getArrayCopy();
        } elseif ($other instanceof Traversable) {
            return iterator_to_array($other);
        }

        // Keep BC even if we know that array would not be the expected one
        return (array) $other;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Framework_Constraint_Attribute extends PHPUnit_Framework_Constraint_Composite
{
    /**
     * @var string
     */
    protected $attributeName;

    /**
     * @param PHPUnit_Framework_Constraint $constraint
     * @param string                       $attributeName
     */
    public function __construct(PHPUnit_Framework_Constraint $constraint, $attributeName)
    {
        parent::__construct($constraint);

        $this->attributeName = $attributeName;
    }

    /**
     * Evaluates the constraint for parameter $other
     *
     * If $returnResult is set to false (the default), an exception is thrown
     * in case of a failure. null is returned otherwise.
     *
     * If $returnResult is true, the result of the evaluation is returned as
     * a boolean value instead: true in case of success, false in case of a
     * failure.
     *
     * @param mixed  $other        Value or object to evaluate.
     * @param string $description  Additional information about the test
     * @param bool   $returnResult Whether to return a result or throw an exception
     *
     * @return mixed
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function evaluate($other, $description = '', $returnResult = false)
    {
        return parent::evaluate(
            PHPUnit_Framework_Assert::readAttribute(
                $other,
                $this->attributeName
            ),
            $description,
            $returnResult
        );
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'attribute "' . $this->attributeName . '" ' .
               $this->innerConstraint->toString();
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        return $this->toString();
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that evaluates against a specified closure.
 */
class PHPUnit_Framework_Constraint_Callback extends PHPUnit_Framework_Constraint
{
    private $callback;

    /**
     * @param callable $callback
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function __construct($callback)
    {
        if (!is_callable($callback)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(
                1,
                'callable'
            );
        }

        parent::__construct();

        $this->callback = $callback;
    }

    /**
     * Evaluates the constraint for parameter $value. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return call_user_func($this->callback, $other);
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'is accepted by specified callback';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that asserts that the class it is evaluated for has a given
 * attribute.
 *
 * The attribute name is passed in the constructor.
 */
class PHPUnit_Framework_Constraint_ClassHasAttribute extends PHPUnit_Framework_Constraint
{
    /**
     * @var string
     */
    protected $attributeName;

    /**
     * @param string $attributeName
     */
    public function __construct($attributeName)
    {
        parent::__construct();
        $this->attributeName = $attributeName;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        $class = new ReflectionClass($other);

        return $class->hasProperty($this->attributeName);
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return sprintf(
            'has attribute "%s"',
            $this->attributeName
        );
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        return sprintf(
            '%sclass "%s" %s',
            is_object($other) ? 'object of ' : '',
            is_object($other) ? get_class($other) : $other,
            $this->toString()
        );
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that asserts that the class it is evaluated for has a given
 * static attribute.
 *
 * The attribute name is passed in the constructor.
 */
class PHPUnit_Framework_Constraint_ClassHasStaticAttribute extends PHPUnit_Framework_Constraint_ClassHasAttribute
{
    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        $class = new ReflectionClass($other);

        if ($class->hasProperty($this->attributeName)) {
            $attribute = $class->getProperty($this->attributeName);

            return $attribute->isStatic();
        } else {
            return false;
        }
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return sprintf(
            'has static attribute "%s"',
            $this->attributeName
        );
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

abstract class PHPUnit_Framework_Constraint_Composite extends PHPUnit_Framework_Constraint
{
    /**
     * @var PHPUnit_Framework_Constraint
     */
    protected $innerConstraint;

    /**
     * @param PHPUnit_Framework_Constraint $innerConstraint
     */
    public function __construct(PHPUnit_Framework_Constraint $innerConstraint)
    {
        parent::__construct();
        $this->innerConstraint = $innerConstraint;
    }

    /**
     * Evaluates the constraint for parameter $other
     *
     * If $returnResult is set to false (the default), an exception is thrown
     * in case of a failure. null is returned otherwise.
     *
     * If $returnResult is true, the result of the evaluation is returned as
     * a boolean value instead: true in case of success, false in case of a
     * failure.
     *
     * @param mixed  $other        Value or object to evaluate.
     * @param string $description  Additional information about the test
     * @param bool   $returnResult Whether to return a result or throw an exception
     *
     * @return mixed
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function evaluate($other, $description = '', $returnResult = false)
    {
        try {
            return $this->innerConstraint->evaluate(
                $other,
                $description,
                $returnResult
            );
        } catch (PHPUnit_Framework_ExpectationFailedException $e) {
            $this->fail($other, $description);
        }
    }

    /**
     * Counts the number of constraint elements.
     *
     * @return int
     */
    public function count()
    {
        return count($this->innerConstraint);
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Framework_Constraint_Count extends PHPUnit_Framework_Constraint
{
    /**
     * @var int
     */
    protected $expectedCount = 0;

    /**
     * @param int $expected
     */
    public function __construct($expected)
    {
        parent::__construct();
        $this->expectedCount = $expected;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other
     *
     * @return bool
     */
    protected function matches($other)
    {
        return $this->expectedCount === $this->getCountOf($other);
    }

    /**
     * @param mixed $other
     *
     * @return bool
     */
    protected function getCountOf($other)
    {
        if ($other instanceof Countable || is_array($other)) {
            return count($other);
        } elseif ($other instanceof Traversable) {
            if ($other instanceof IteratorAggregate) {
                $iterator = $other->getIterator();
            } else {
                $iterator = $other;
            }

            if ($iterator instanceof Generator) {
                return $this->getCountOfGenerator($iterator);
            }

            $key   = $iterator->key();
            $count = iterator_count($iterator);

            // Manually rewind $iterator to previous key, since iterator_count
            // moves pointer.
            if ($key !== null) {
                $iterator->rewind();
                while ($iterator->valid() && $key !== $iterator->key()) {
                    $iterator->next();
                }
            }

            return $count;
        }
    }

    /**
     * Returns the total number of iterations from a generator.
     * This will fully exhaust the generator.
     *
     * @param Generator $generator
     *
     * @return int
     */
    protected function getCountOfGenerator(Generator $generator)
    {
        for ($count = 0; $generator->valid(); $generator->next()) {
            $count += 1;
        }

        return $count;
    }

    /**
     * Returns the description of the failure.
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        return sprintf(
            'actual size %d matches expected size %d',
            $this->getCountOf($other),
            $this->expectedCount
        );
    }

    /**
     * @return string
     */
    public function toString()
    {
        return sprintf(
            'count matches %d',
            $this->expectedCount
        );
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that checks if the directory(name) that it is evaluated for exists.
 *
 * The file path to check is passed as $other in evaluate().
 */
class PHPUnit_Framework_Constraint_DirectoryExists extends PHPUnit_Framework_Constraint
{
    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return is_dir($other);
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        return sprintf(
            'directory "%s" exists',
            $other
        );
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'directory exists';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Framework_Constraint_Exception extends PHPUnit_Framework_Constraint
{
    /**
     * @var string
     */
    protected $className;

    /**
     * @param string $className
     */
    public function __construct($className)
    {
        parent::__construct();
        $this->className = $className;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return $other instanceof $this->className;
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        if ($other !== null) {
            $message = '';
            if ($other instanceof Exception || $other instanceof Throwable) {
                $message = '. Message was: "' . $other->getMessage() . '" at'
                        . "\n" . PHPUnit_Util_Filter::getFilteredStacktrace($other);
            }

            return sprintf(
                'exception of type "%s" matches expected exception "%s"%s',
                get_class($other),
                $this->className,
                $message
            );
        }

        return sprintf(
            'exception of type "%s" is thrown',
            $this->className
        );
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return sprintf(
            'exception of type "%s"',
            $this->className
        );
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Framework_Constraint_ExceptionCode extends PHPUnit_Framework_Constraint
{
    /**
     * @var int
     */
    protected $expectedCode;

    /**
     * @param int $expected
     */
    public function __construct($expected)
    {
        parent::__construct();
        $this->expectedCode = $expected;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param Exception $other
     *
     * @return bool
     */
    protected function matches($other)
    {
        return (string) $other->getCode() == (string) $this->expectedCode;
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        return sprintf(
            '%s is equal to expected exception code %s',
            $this->exporter->export($other->getCode()),
            $this->exporter->export($this->expectedCode)
        );
    }

    /**
     * @return string
     */
    public function toString()
    {
        return 'exception code is ';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Framework_Constraint_ExceptionMessage extends PHPUnit_Framework_Constraint
{
    /**
     * @var int
     */
    protected $expectedMessage;

    /**
     * @param string $expected
     */
    public function __construct($expected)
    {
        parent::__construct();
        $this->expectedMessage = $expected;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param Exception $other
     *
     * @return bool
     */
    protected function matches($other)
    {
        return strpos($other->getMessage(), $this->expectedMessage) !== false;
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        return sprintf(
            "exception message '%s' contains '%s'",
            $other->getMessage(),
            $this->expectedMessage
        );
    }

    /**
     * @return string
     */
    public function toString()
    {
        return 'exception message contains ';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Framework_Constraint_ExceptionMessageRegExp extends PHPUnit_Framework_Constraint
{
    /**
     * @var int
     */
    protected $expectedMessageRegExp;

    /**
     * @param string $expected
     */
    public function __construct($expected)
    {
        parent::__construct();
        $this->expectedMessageRegExp = $expected;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param Exception $other
     *
     * @return bool
     */
    protected function matches($other)
    {
        $match = PHPUnit_Util_Regex::pregMatchSafe($this->expectedMessageRegExp, $other->getMessage());

        if (false === $match) {
            throw new PHPUnit_Framework_Exception(
                "Invalid expected exception message regex given: '{$this->expectedMessageRegExp}'"
            );
        }

        return 1 === $match;
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        return sprintf(
            "exception message '%s' matches '%s'",
            $other->getMessage(),
            $this->expectedMessageRegExp
        );
    }

    /**
     * @return string
     */
    public function toString()
    {
        return 'exception message matches ';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that checks if the file(name) that it is evaluated for exists.
 *
 * The file path to check is passed as $other in evaluate().
 */
class PHPUnit_Framework_Constraint_FileExists extends PHPUnit_Framework_Constraint
{
    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return file_exists($other);
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        return sprintf(
            'file "%s" exists',
            $other
        );
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'file exists';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that asserts that the value it is evaluated for is greater
 * than a given value.
 */
class PHPUnit_Framework_Constraint_GreaterThan extends PHPUnit_Framework_Constraint
{
    /**
     * @var numeric
     */
    protected $value;

    /**
     * @param numeric $value
     */
    public function __construct($value)
    {
        parent::__construct();
        $this->value = $value;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return $this->value < $other;
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'is greater than ' . $this->exporter->export($this->value);
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that accepts any input value.
 */
class PHPUnit_Framework_Constraint_IsAnything extends PHPUnit_Framework_Constraint
{
    /**
     * Evaluates the constraint for parameter $other
     *
     * If $returnResult is set to false (the default), an exception is thrown
     * in case of a failure. null is returned otherwise.
     *
     * If $returnResult is true, the result of the evaluation is returned as
     * a boolean value instead: true in case of success, false in case of a
     * failure.
     *
     * @param mixed  $other        Value or object to evaluate.
     * @param string $description  Additional information about the test
     * @param bool   $returnResult Whether to return a result or throw an exception
     *
     * @return mixed
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function evaluate($other, $description = '', $returnResult = false)
    {
        return $returnResult ? true : null;
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'is anything';
    }

    /**
     * Counts the number of constraint elements.
     *
     * @return int
     */
    public function count()
    {
        return 0;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that checks whether a variable is empty().
 */
class PHPUnit_Framework_Constraint_IsEmpty extends PHPUnit_Framework_Constraint
{
    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        if ($other instanceof Countable) {
            return count($other) === 0;
        }

        return empty($other);
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'is empty';
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        $type = gettype($other);

        return sprintf(
            '%s %s %s',
            $type[0] == 'a' || $type[0] == 'o' ? 'an' : 'a',
            $type,
            $this->toString()
        );
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that checks if one value is equal to another.
 *
 * Equality is checked with PHP's == operator, the operator is explained in
 * detail at {@url http://www.php.net/manual/en/types.comparisons.php}.
 * Two values are equal if they have the same value disregarding type.
 *
 * The expected value is passed in the constructor.
 */
class PHPUnit_Framework_Constraint_IsEqual extends PHPUnit_Framework_Constraint
{
    /**
     * @var mixed
     */
    protected $value;

    /**
     * @var float
     */
    protected $delta = 0.0;

    /**
     * @var int
     */
    protected $maxDepth = 10;

    /**
     * @var bool
     */
    protected $canonicalize = false;

    /**
     * @var bool
     */
    protected $ignoreCase = false;

    /**
     * @var SebastianBergmann\Comparator\ComparisonFailure
     */
    protected $lastFailure;

    /**
     * @param mixed $value
     * @param float $delta
     * @param int   $maxDepth
     * @param bool  $canonicalize
     * @param bool  $ignoreCase
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function __construct($value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
    {
        parent::__construct();

        if (!is_numeric($delta)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'numeric');
        }

        if (!is_int($maxDepth)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'integer');
        }

        if (!is_bool($canonicalize)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(4, 'boolean');
        }

        if (!is_bool($ignoreCase)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(5, 'boolean');
        }

        $this->value        = $value;
        $this->delta        = $delta;
        $this->maxDepth     = $maxDepth;
        $this->canonicalize = $canonicalize;
        $this->ignoreCase   = $ignoreCase;
    }

    /**
     * Evaluates the constraint for parameter $other
     *
     * If $returnResult is set to false (the default), an exception is thrown
     * in case of a failure. null is returned otherwise.
     *
     * If $returnResult is true, the result of the evaluation is returned as
     * a boolean value instead: true in case of success, false in case of a
     * failure.
     *
     * @param mixed  $other        Value or object to evaluate.
     * @param string $description  Additional information about the test
     * @param bool   $returnResult Whether to return a result or throw an exception
     *
     * @return mixed
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function evaluate($other, $description = '', $returnResult = false)
    {
        // If $this->value and $other are identical, they are also equal.
        // This is the most common path and will allow us to skip
        // initialization of all the comparators.
        if ($this->value === $other) {
            return true;
        }

        $comparatorFactory = SebastianBergmann\Comparator\Factory::getInstance();

        try {
            $comparator = $comparatorFactory->getComparatorFor(
                $this->value,
                $other
            );

            $comparator->assertEquals(
                $this->value,
                $other,
                $this->delta,
                $this->canonicalize,
                $this->ignoreCase
            );
        } catch (SebastianBergmann\Comparator\ComparisonFailure $f) {
            if ($returnResult) {
                return false;
            }

            throw new PHPUnit_Framework_ExpectationFailedException(
                trim($description . "\n" . $f->getMessage()),
                $f
            );
        }

        return true;
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        $delta = '';

        if (is_string($this->value)) {
            if (strpos($this->value, "\n") !== false) {
                return 'is equal to <text>';
            } else {
                return sprintf(
                    'is equal to <string:%s>',
                    $this->value
                );
            }
        } else {
            if ($this->delta != 0) {
                $delta = sprintf(
                    ' with delta <%F>',
                    $this->delta
                );
            }

            return sprintf(
                'is equal to %s%s',
                $this->exporter->export($this->value),
                $delta
            );
        }
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that accepts false.
 */
class PHPUnit_Framework_Constraint_IsFalse extends PHPUnit_Framework_Constraint
{
    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return $other === false;
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'is false';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that accepts finite.
 */
class PHPUnit_Framework_Constraint_IsFinite extends PHPUnit_Framework_Constraint
{
    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return is_finite($other);
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'is finite';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that asserts that one value is identical to another.
 *
 * Identical check is performed with PHP's === operator, the operator is
 * explained in detail at
 * {@url http://www.php.net/manual/en/types.comparisons.php}.
 * Two values are identical if they have the same value and are of the same
 * type.
 *
 * The expected value is passed in the constructor.
 */
class PHPUnit_Framework_Constraint_IsIdentical extends PHPUnit_Framework_Constraint
{
    /**
     * @var float
     */
    const EPSILON = 0.0000000001;

    /**
     * @var mixed
     */
    protected $value;

    /**
     * @param mixed $value
     */
    public function __construct($value)
    {
        parent::__construct();
        $this->value = $value;
    }

    /**
     * Evaluates the constraint for parameter $other
     *
     * If $returnResult is set to false (the default), an exception is thrown
     * in case of a failure. null is returned otherwise.
     *
     * If $returnResult is true, the result of the evaluation is returned as
     * a boolean value instead: true in case of success, false in case of a
     * failure.
     *
     * @param mixed  $other        Value or object to evaluate.
     * @param string $description  Additional information about the test
     * @param bool   $returnResult Whether to return a result or throw an exception
     *
     * @return mixed
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function evaluate($other, $description = '', $returnResult = false)
    {
        if (is_float($this->value) && is_float($other) &&
            !is_infinite($this->value) && !is_infinite($other) &&
            !is_nan($this->value) && !is_nan($other)) {
            $success = abs($this->value - $other) < self::EPSILON;
        } else {
            $success = $this->value === $other;
        }

        if ($returnResult) {
            return $success;
        }

        if (!$success) {
            $f = null;

            // if both values are strings, make sure a diff is generated
            if (is_string($this->value) && is_string($other)) {
                $f = new SebastianBergmann\Comparator\ComparisonFailure(
                    $this->value,
                    $other,
                    $this->value,
                    $other
                );
            }

            $this->fail($other, $description, $f);
        }
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        if (is_object($this->value) && is_object($other)) {
            return 'two variables reference the same object';
        }

        if (is_string($this->value) && is_string($other)) {
            return 'two strings are identical';
        }

        return parent::failureDescription($other);
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        if (is_object($this->value)) {
            return 'is identical to an object of class "' .
                   get_class($this->value) . '"';
        } else {
            return 'is identical to ' .
                   $this->exporter->export($this->value);
        }
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that accepts infinite.
 */
class PHPUnit_Framework_Constraint_IsInfinite extends PHPUnit_Framework_Constraint
{
    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return is_infinite($other);
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'is infinite';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that asserts that the object it is evaluated for is an instance
 * of a given class.
 *
 * The expected class name is passed in the constructor.
 */
class PHPUnit_Framework_Constraint_IsInstanceOf extends PHPUnit_Framework_Constraint
{
    /**
     * @var string
     */
    protected $className;

    /**
     * @param string $className
     */
    public function __construct($className)
    {
        parent::__construct();
        $this->className = $className;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return ($other instanceof $this->className);
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        return sprintf(
            '%s is an instance of %s "%s"',
            $this->exporter->shortenedExport($other),
            $this->getType(),
            $this->className
        );
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return sprintf(
            'is instance of %s "%s"',
            $this->getType(),
            $this->className
        );
    }

    private function getType()
    {
        try {
            $reflection = new ReflectionClass($this->className);
            if ($reflection->isInterface()) {
                return 'interface';
            }
        } catch (ReflectionException $e) {
        }

        return 'class';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that asserts that a string is valid JSON.
 */
class PHPUnit_Framework_Constraint_IsJson extends PHPUnit_Framework_Constraint
{
    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        if ($other === '') {
            return false;
        }

        json_decode($other);
        if (json_last_error()) {
            return false;
        }

        return true;
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        if ($other === '') {
            return 'an empty string is valid JSON';
        }

        json_decode($other);
        $error = PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider::determineJsonError(
            json_last_error()
        );

        return sprintf(
            '%s is valid JSON (%s)',
            $this->exporter->shortenedExport($other),
            $error
        );
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'is valid JSON';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that accepts nan.
 */
class PHPUnit_Framework_Constraint_IsNan extends PHPUnit_Framework_Constraint
{
    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return is_nan($other);
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'is nan';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that accepts null.
 */
class PHPUnit_Framework_Constraint_IsNull extends PHPUnit_Framework_Constraint
{
    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return $other === null;
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'is null';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that checks if the file/dir(name) that it is evaluated for is readable.
 *
 * The file path to check is passed as $other in evaluate().
 */
class PHPUnit_Framework_Constraint_IsReadable extends PHPUnit_Framework_Constraint
{
    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return is_readable($other);
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        return sprintf(
            '"%s" is readable',
            $other
        );
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'is readable';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that accepts true.
 */
class PHPUnit_Framework_Constraint_IsTrue extends PHPUnit_Framework_Constraint
{
    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return $other === true;
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'is true';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that asserts that the value it is evaluated for is of a
 * specified type.
 *
 * The expected value is passed in the constructor.
 */
class PHPUnit_Framework_Constraint_IsType extends PHPUnit_Framework_Constraint
{
    const TYPE_ARRAY    = 'array';
    const TYPE_BOOL     = 'bool';
    const TYPE_FLOAT    = 'float';
    const TYPE_INT      = 'int';
    const TYPE_NULL     = 'null';
    const TYPE_NUMERIC  = 'numeric';
    const TYPE_OBJECT   = 'object';
    const TYPE_RESOURCE = 'resource';
    const TYPE_STRING   = 'string';
    const TYPE_SCALAR   = 'scalar';
    const TYPE_CALLABLE = 'callable';

    /**
     * @var array
     */
    protected $types = [
        'array'    => true,
        'boolean'  => true,
        'bool'     => true,
        'double'   => true,
        'float'    => true,
        'integer'  => true,
        'int'      => true,
        'null'     => true,
        'numeric'  => true,
        'object'   => true,
        'real'     => true,
        'resource' => true,
        'string'   => true,
        'scalar'   => true,
        'callable' => true
    ];

    /**
     * @var string
     */
    protected $type;

    /**
     * @param string $type
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function __construct($type)
    {
        parent::__construct();

        if (!isset($this->types[$type])) {
            throw new PHPUnit_Framework_Exception(
                sprintf(
                    'Type specified for PHPUnit_Framework_Constraint_IsType <%s> ' .
                    'is not a valid type.',
                    $type
                )
            );
        }

        $this->type = $type;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        switch ($this->type) {
            case 'numeric':
                return is_numeric($other);

            case 'integer':
            case 'int':
                return is_int($other);

            case 'double':
            case 'float':
            case 'real':
                return is_float($other);

            case 'string':
                return is_string($other);

            case 'boolean':
            case 'bool':
                return is_bool($other);

            case 'null':
                return is_null($other);

            case 'array':
                return is_array($other);

            case 'object':
                return is_object($other);

            case 'resource':
                return is_resource($other) || is_string(@get_resource_type($other));

            case 'scalar':
                return is_scalar($other);

            case 'callable':
                return is_callable($other);
        }
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return sprintf(
            'is of type "%s"',
            $this->type
        );
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that checks if the file/dir(name) that it is evaluated for is writable.
 *
 * The file path to check is passed as $other in evaluate().
 */
class PHPUnit_Framework_Constraint_IsWritable extends PHPUnit_Framework_Constraint
{
    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return is_writable($other);
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        return sprintf(
            '"%s" is writable',
            $other
        );
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'is writable';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Provides human readable messages for each JSON error.
 */
class PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider
{
    /**
     * Translates JSON error to a human readable string.
     *
     * @param string $error
     * @param string $prefix
     *
     * @return string
     */
    public static function determineJsonError($error, $prefix = '')
    {
        switch ($error) {
            case JSON_ERROR_NONE:
                return;
            case JSON_ERROR_DEPTH:
                return $prefix . 'Maximum stack depth exceeded';
            case JSON_ERROR_STATE_MISMATCH:
                return $prefix . 'Underflow or the modes mismatch';
            case JSON_ERROR_CTRL_CHAR:
                return $prefix . 'Unexpected control character found';
            case JSON_ERROR_SYNTAX:
                return $prefix . 'Syntax error, malformed JSON';
            case JSON_ERROR_UTF8:
                return $prefix . 'Malformed UTF-8 characters, possibly incorrectly encoded';
            default:
                return $prefix . 'Unknown error';
        }
    }

    /**
     * Translates a given type to a human readable message prefix.
     *
     * @param string $type
     *
     * @return string
     */
    public static function translateTypeToPrefix($type)
    {
        switch (strtolower($type)) {
            case 'expected':
                $prefix = 'Expected value JSON decode error - ';
                break;
            case 'actual':
                $prefix = 'Actual value JSON decode error - ';
                break;
            default:
                $prefix = '';
                break;
        }

        return $prefix;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Asserts whether or not two JSON objects are equal.
 */
class PHPUnit_Framework_Constraint_JsonMatches extends PHPUnit_Framework_Constraint
{
    /**
     * @var string
     */
    protected $value;

    /**
     * Creates a new constraint.
     *
     * @param string $value
     */
    public function __construct($value)
    {
        parent::__construct();
        $this->value = $value;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * This method can be overridden to implement the evaluation algorithm.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        $decodedOther = json_decode($other);
        if (json_last_error()) {
            return false;
        }

        $decodedValue = json_decode($this->value);
        if (json_last_error()) {
            return false;
        }

        return $decodedOther == $decodedValue;
    }

    /**
     * Returns a string representation of the object.
     *
     * @return string
     */
    public function toString()
    {
        return sprintf(
            'matches JSON string "%s"',
            $this->value
        );
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that asserts that the value it is evaluated for is less than
 * a given value.
 */
class PHPUnit_Framework_Constraint_LessThan extends PHPUnit_Framework_Constraint
{
    /**
     * @var numeric
     */
    protected $value;

    /**
     * @param numeric $value
     */
    public function __construct($value)
    {
        parent::__construct();
        $this->value = $value;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return $this->value > $other;
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'is less than ' . $this->exporter->export($this->value);
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Logical NOT.
 */
class PHPUnit_Framework_Constraint_Not extends PHPUnit_Framework_Constraint
{
    /**
     * @var PHPUnit_Framework_Constraint
     */
    protected $constraint;

    /**
     * @param PHPUnit_Framework_Constraint $constraint
     */
    public function __construct($constraint)
    {
        parent::__construct();

        if (!($constraint instanceof PHPUnit_Framework_Constraint)) {
            $constraint = new PHPUnit_Framework_Constraint_IsEqual($constraint);
        }

        $this->constraint = $constraint;
    }

    /**
     * @param string $string
     *
     * @return string
     */
    public static function negate($string)
    {
        return str_replace(
            [
            'contains ',
            'exists',
            'has ',
            'is ',
            'are ',
            'matches ',
            'starts with ',
            'ends with ',
            'reference ',
            'not not '
            ],
            [
            'does not contain ',
            'does not exist',
            'does not have ',
            'is not ',
            'are not ',
            'does not match ',
            'starts not with ',
            'ends not with ',
            'don\'t reference ',
            'not '
            ],
            $string
        );
    }

    /**
     * Evaluates the constraint for parameter $other
     *
     * If $returnResult is set to false (the default), an exception is thrown
     * in case of a failure. null is returned otherwise.
     *
     * If $returnResult is true, the result of the evaluation is returned as
     * a boolean value instead: true in case of success, false in case of a
     * failure.
     *
     * @param mixed  $other        Value or object to evaluate.
     * @param string $description  Additional information about the test
     * @param bool   $returnResult Whether to return a result or throw an exception
     *
     * @return mixed
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function evaluate($other, $description = '', $returnResult = false)
    {
        $success = !$this->constraint->evaluate($other, $description, true);

        if ($returnResult) {
            return $success;
        }

        if (!$success) {
            $this->fail($other, $description);
        }
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        switch (get_class($this->constraint)) {
            case 'PHPUnit_Framework_Constraint_And':
            case 'PHPUnit_Framework_Constraint_Not':
            case 'PHPUnit_Framework_Constraint_Or':
                return 'not( ' . $this->constraint->failureDescription($other) . ' )';

            default:
                return self::negate(
                    $this->constraint->failureDescription($other)
                );
        }
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        switch (get_class($this->constraint)) {
            case 'PHPUnit_Framework_Constraint_And':
            case 'PHPUnit_Framework_Constraint_Not':
            case 'PHPUnit_Framework_Constraint_Or':
                return 'not( ' . $this->constraint->toString() . ' )';

            default:
                return self::negate(
                    $this->constraint->toString()
                );
        }
    }

    /**
     * Counts the number of constraint elements.
     *
     * @return int
     */
    public function count()
    {
        return count($this->constraint);
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that asserts that the object it is evaluated for has a given
 * attribute.
 *
 * The attribute name is passed in the constructor.
 */
class PHPUnit_Framework_Constraint_ObjectHasAttribute extends PHPUnit_Framework_Constraint_ClassHasAttribute
{
    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        $object = new ReflectionObject($other);

        return $object->hasProperty($this->attributeName);
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Logical OR.
 */
class PHPUnit_Framework_Constraint_Or extends PHPUnit_Framework_Constraint
{
    /**
     * @var PHPUnit_Framework_Constraint[]
     */
    protected $constraints = [];

    /**
     * @param PHPUnit_Framework_Constraint[] $constraints
     */
    public function setConstraints(array $constraints)
    {
        $this->constraints = [];

        foreach ($constraints as $constraint) {
            if (!($constraint instanceof PHPUnit_Framework_Constraint)) {
                $constraint = new PHPUnit_Framework_Constraint_IsEqual(
                    $constraint
                );
            }

            $this->constraints[] = $constraint;
        }
    }

    /**
     * Evaluates the constraint for parameter $other
     *
     * If $returnResult is set to false (the default), an exception is thrown
     * in case of a failure. null is returned otherwise.
     *
     * If $returnResult is true, the result of the evaluation is returned as
     * a boolean value instead: true in case of success, false in case of a
     * failure.
     *
     * @param mixed  $other        Value or object to evaluate.
     * @param string $description  Additional information about the test
     * @param bool   $returnResult Whether to return a result or throw an exception
     *
     * @return mixed
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function evaluate($other, $description = '', $returnResult = false)
    {
        $success    = false;
        $constraint = null;

        foreach ($this->constraints as $constraint) {
            if ($constraint->evaluate($other, $description, true)) {
                $success = true;
                break;
            }
        }

        if ($returnResult) {
            return $success;
        }

        if (!$success) {
            $this->fail($other, $description);
        }
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        $text = '';

        foreach ($this->constraints as $key => $constraint) {
            if ($key > 0) {
                $text .= ' or ';
            }

            $text .= $constraint->toString();
        }

        return $text;
    }

    /**
     * Counts the number of constraint elements.
     *
     * @return int
     */
    public function count()
    {
        $count = 0;

        foreach ($this->constraints as $constraint) {
            $count += count($constraint);
        }

        return $count;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that asserts that the string it is evaluated for matches
 * a regular expression.
 *
 * Checks a given value using the Perl Compatible Regular Expression extension
 * in PHP. The pattern is matched by executing preg_match().
 *
 * The pattern string passed in the constructor.
 */
class PHPUnit_Framework_Constraint_PCREMatch extends PHPUnit_Framework_Constraint
{
    /**
     * @var string
     */
    protected $pattern;

    /**
     * @param string $pattern
     */
    public function __construct($pattern)
    {
        parent::__construct();
        $this->pattern = $pattern;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return preg_match($this->pattern, $other) > 0;
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return sprintf(
            'matches PCRE pattern "%s"',
            $this->pattern
        );
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Framework_Constraint_SameSize extends PHPUnit_Framework_Constraint_Count
{
    /**
     * @var int
     */
    protected $expectedCount;

    /**
     * @param int $expected
     */
    public function __construct($expected)
    {
        parent::__construct($this->getCountOf($expected));
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that asserts that the string it is evaluated for contains
 * a given string.
 *
 * Uses mb_strpos() to find the position of the string in the input, if not
 * found the evaluation fails.
 *
 * The sub-string is passed in the constructor.
 */
class PHPUnit_Framework_Constraint_StringContains extends PHPUnit_Framework_Constraint
{
    /**
     * @var string
     */
    protected $string;

    /**
     * @var bool
     */
    protected $ignoreCase;

    /**
     * @param string $string
     * @param bool   $ignoreCase
     */
    public function __construct($string, $ignoreCase = false)
    {
        parent::__construct();

        $this->string     = $string;
        $this->ignoreCase = $ignoreCase;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        if ($this->ignoreCase) {
            return mb_stripos($other, $this->string) !== false;
        } else {
            return mb_strpos($other, $this->string) !== false;
        }
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        if ($this->ignoreCase) {
            $string = mb_strtolower($this->string);
        } else {
            $string = $this->string;
        }

        return sprintf(
            'contains "%s"',
            $string
        );
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that asserts that the string it is evaluated for ends with a given
 * suffix.
 */
class PHPUnit_Framework_Constraint_StringEndsWith extends PHPUnit_Framework_Constraint
{
    /**
     * @var string
     */
    protected $suffix;

    /**
     * @param string $suffix
     */
    public function __construct($suffix)
    {
        parent::__construct();
        $this->suffix = $suffix;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return substr($other, 0 - strlen($this->suffix)) == $this->suffix;
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'ends with "' . $this->suffix . '"';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use SebastianBergmann\Diff\Differ;

/**
 * ...
 */
class PHPUnit_Framework_Constraint_StringMatches extends PHPUnit_Framework_Constraint_PCREMatch
{
    /**
     * @var string
     */
    protected $string;

    /**
     * @param string $string
     */
    public function __construct($string)
    {
        parent::__construct($string);

        $this->pattern = $this->createPatternFromFormat(
            preg_replace('/\r\n/', "\n", $string)
        );

        $this->string = $string;
    }

    protected function failureDescription($other)
    {
        return 'format description matches text';
    }

    protected function additionalFailureDescription($other)
    {
        $from = preg_split('(\r\n|\r|\n)', $this->string);
        $to   = preg_split('(\r\n|\r|\n)', $other);

        foreach ($from as $index => $line) {
            if (isset($to[$index]) && $line !== $to[$index]) {
                $line = $this->createPatternFromFormat($line);

                if (preg_match($line, $to[$index]) > 0) {
                    $from[$index] = $to[$index];
                }
            }
        }

        $this->string = implode("\n", $from);
        $other        = implode("\n", $to);

        $differ = new Differ("--- Expected\n+++ Actual\n");

        return $differ->diff($this->string, $other);
    }

    protected function createPatternFromFormat($string)
    {
        $string = str_replace(
            [
            '%e',
            '%s',
            '%S',
            '%a',
            '%A',
            '%w',
            '%i',
            '%d',
            '%x',
            '%f',
            '%c'
            ],
            [
            '\\' . DIRECTORY_SEPARATOR,
            '[^\r\n]+',
            '[^\r\n]*',
            '.+',
            '.*',
            '\s*',
            '[+-]?\d+',
            '\d+',
            '[0-9a-fA-F]+',
            '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?',
            '.'
            ],
            preg_quote($string, '/')
        );

        return '/^' . $string . '$/s';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that asserts that the string it is evaluated for begins with a
 * given prefix.
 */
class PHPUnit_Framework_Constraint_StringStartsWith extends PHPUnit_Framework_Constraint
{
    /**
     * @var string
     */
    protected $prefix;

    /**
     * @param string $prefix
     */
    public function __construct($prefix)
    {
        parent::__construct();
        $this->prefix = $prefix;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return strpos($other, $this->prefix) === 0;
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'starts with "' . $this->prefix . '"';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that asserts that the Traversable it is applied to contains
 * a given value.
 */
class PHPUnit_Framework_Constraint_TraversableContains extends PHPUnit_Framework_Constraint
{
    /**
     * @var bool
     */
    protected $checkForObjectIdentity;

    /**
     * @var bool
     */
    protected $checkForNonObjectIdentity;

    /**
     * @var mixed
     */
    protected $value;

    /**
     * @param mixed $value
     * @param bool  $checkForObjectIdentity
     * @param bool  $checkForNonObjectIdentity
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function __construct($value, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
    {
        parent::__construct();

        if (!is_bool($checkForObjectIdentity)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'boolean');
        }

        if (!is_bool($checkForNonObjectIdentity)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'boolean');
        }

        $this->checkForObjectIdentity    = $checkForObjectIdentity;
        $this->checkForNonObjectIdentity = $checkForNonObjectIdentity;
        $this->value                     = $value;
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        if ($other instanceof SplObjectStorage) {
            return $other->contains($this->value);
        }

        if (is_object($this->value)) {
            foreach ($other as $element) {
                if ($this->checkForObjectIdentity && $element === $this->value) {
                    return true;
                } elseif (!$this->checkForObjectIdentity && $element == $this->value) {
                    return true;
                }
            }
        } else {
            foreach ($other as $element) {
                if ($this->checkForNonObjectIdentity && $element === $this->value) {
                    return true;
                } elseif (!$this->checkForNonObjectIdentity && $element == $this->value) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        if (is_string($this->value) && strpos($this->value, "\n") !== false) {
            return 'contains "' . $this->value . '"';
        } else {
            return 'contains ' . $this->exporter->export($this->value);
        }
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        return sprintf(
            '%s %s',
            is_array($other) ? 'an array' : 'a traversable',
            $this->toString()
        );
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Constraint that asserts that the Traversable it is applied to contains
 * only values of a given type.
 */
class PHPUnit_Framework_Constraint_TraversableContainsOnly extends PHPUnit_Framework_Constraint
{
    /**
     * @var PHPUnit_Framework_Constraint
     */
    protected $constraint;

    /**
     * @var string
     */
    protected $type;

    /**
     * @param string $type
     * @param bool   $isNativeType
     */
    public function __construct($type, $isNativeType = true)
    {
        parent::__construct();

        if ($isNativeType) {
            $this->constraint = new PHPUnit_Framework_Constraint_IsType($type);
        } else {
            $this->constraint = new PHPUnit_Framework_Constraint_IsInstanceOf(
                $type
            );
        }

        $this->type = $type;
    }

    /**
     * Evaluates the constraint for parameter $other
     *
     * If $returnResult is set to false (the default), an exception is thrown
     * in case of a failure. null is returned otherwise.
     *
     * If $returnResult is true, the result of the evaluation is returned as
     * a boolean value instead: true in case of success, false in case of a
     * failure.
     *
     * @param mixed  $other        Value or object to evaluate.
     * @param string $description  Additional information about the test
     * @param bool   $returnResult Whether to return a result or throw an exception
     *
     * @return mixed
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function evaluate($other, $description = '', $returnResult = false)
    {
        $success = true;

        foreach ($other as $item) {
            if (!$this->constraint->evaluate($item, '', true)) {
                $success = false;
                break;
            }
        }

        if ($returnResult) {
            return $success;
        }

        if (!$success) {
            $this->fail($other, $description);
        }
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return 'contains only values of type "' . $this->type . '"';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Logical XOR.
 */
class PHPUnit_Framework_Constraint_Xor extends PHPUnit_Framework_Constraint
{
    /**
     * @var PHPUnit_Framework_Constraint[]
     */
    protected $constraints = [];

    /**
     * @param PHPUnit_Framework_Constraint[] $constraints
     */
    public function setConstraints(array $constraints)
    {
        $this->constraints = [];

        foreach ($constraints as $constraint) {
            if (!($constraint instanceof PHPUnit_Framework_Constraint)) {
                $constraint = new PHPUnit_Framework_Constraint_IsEqual(
                    $constraint
                );
            }

            $this->constraints[] = $constraint;
        }
    }

    /**
     * Evaluates the constraint for parameter $other
     *
     * If $returnResult is set to false (the default), an exception is thrown
     * in case of a failure. null is returned otherwise.
     *
     * If $returnResult is true, the result of the evaluation is returned as
     * a boolean value instead: true in case of success, false in case of a
     * failure.
     *
     * @param mixed  $other        Value or object to evaluate.
     * @param string $description  Additional information about the test
     * @param bool   $returnResult Whether to return a result or throw an exception
     *
     * @return mixed
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function evaluate($other, $description = '', $returnResult = false)
    {
        $success    = true;
        $lastResult = null;
        $constraint = null;

        foreach ($this->constraints as $constraint) {
            $result = $constraint->evaluate($other, $description, true);

            if ($result === $lastResult) {
                $success = false;
                break;
            }

            $lastResult = $result;
        }

        if ($returnResult) {
            return $success;
        }

        if (!$success) {
            $this->fail($other, $description);
        }
    }

    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        $text = '';

        foreach ($this->constraints as $key => $constraint) {
            if ($key > 0) {
                $text .= ' xor ';
            }

            $text .= $constraint->toString();
        }

        return $text;
    }

    /**
     * Counts the number of constraint elements.
     *
     * @return int
     */
    public function count()
    {
        $count = 0;

        foreach ($this->constraints as $constraint) {
            $count += count($constraint);
        }

        return $count;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use SebastianBergmann\Exporter\Exporter;

/**
 * Abstract base class for constraints which can be applied to any value.
 */
abstract class PHPUnit_Framework_Constraint implements Countable, PHPUnit_Framework_SelfDescribing
{
    protected $exporter;

    public function __construct()
    {
        $this->exporter = new Exporter;
    }

    /**
     * Evaluates the constraint for parameter $other
     *
     * If $returnResult is set to false (the default), an exception is thrown
     * in case of a failure. null is returned otherwise.
     *
     * If $returnResult is true, the result of the evaluation is returned as
     * a boolean value instead: true in case of success, false in case of a
     * failure.
     *
     * @param mixed  $other        Value or object to evaluate.
     * @param string $description  Additional information about the test
     * @param bool   $returnResult Whether to return a result or throw an exception
     *
     * @return mixed
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function evaluate($other, $description = '', $returnResult = false)
    {
        $success = false;

        if ($this->matches($other)) {
            $success = true;
        }

        if ($returnResult) {
            return $success;
        }

        if (!$success) {
            $this->fail($other, $description);
        }
    }

    /**
     * Evaluates the constraint for parameter $other. Returns true if the
     * constraint is met, false otherwise.
     *
     * This method can be overridden to implement the evaluation algorithm.
     *
     * @param mixed $other Value or object to evaluate.
     *
     * @return bool
     */
    protected function matches($other)
    {
        return false;
    }

    /**
     * Counts the number of constraint elements.
     *
     * @return int
     */
    public function count()
    {
        return 1;
    }

    /**
     * Throws an exception for the given compared value and test description
     *
     * @param mixed                                          $other             Evaluated value or object.
     * @param string                                         $description       Additional information about the test
     * @param SebastianBergmann\Comparator\ComparisonFailure $comparisonFailure
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    protected function fail($other, $description, SebastianBergmann\Comparator\ComparisonFailure $comparisonFailure = null)
    {
        $failureDescription = sprintf(
            'Failed asserting that %s.',
            $this->failureDescription($other)
        );

        $additionalFailureDescription = $this->additionalFailureDescription($other);

        if ($additionalFailureDescription) {
            $failureDescription .= "\n" . $additionalFailureDescription;
        }

        if (!empty($description)) {
            $failureDescription = $description . "\n" . $failureDescription;
        }

        throw new PHPUnit_Framework_ExpectationFailedException(
            $failureDescription,
            $comparisonFailure
        );
    }

    /**
     * Return additional failure description where needed
     *
     * The function can be overridden to provide additional failure
     * information like a diff
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function additionalFailureDescription($other)
    {
        return '';
    }

    /**
     * Returns the description of the failure
     *
     * The beginning of failure messages is "Failed asserting that" in most
     * cases. This method should return the second part of that sentence.
     *
     * To provide additional failure information additionalFailureDescription
     * can be used.
     *
     * @param mixed $other Evaluated value or object.
     *
     * @return string
     */
    protected function failureDescription($other)
    {
        return $this->exporter->export($other) . ' ' . $this->toString();
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Extension to PHPUnit_Framework_AssertionFailedError to mark the special
 * case of a test that does not execute the code it wants to cover.
 */
class PHPUnit_Framework_CoveredCodeNotExecutedException extends PHPUnit_Framework_RiskyTestError
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Wrapper for PHP deprecated errors.
 * You can disable deprecated-to-exception conversion by setting
 *
 * <code>
 * PHPUnit_Framework_Error_Deprecated::$enabled = false;
 * </code>
 */
class PHPUnit_Framework_Error_Deprecated extends PHPUnit_Framework_Error
{
    public static $enabled = true;
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Wrapper for PHP notices.
 * You can disable notice-to-exception conversion by setting
 *
 * <code>
 * PHPUnit_Framework_Error_Notice::$enabled = false;
 * </code>
 */
class PHPUnit_Framework_Error_Notice extends PHPUnit_Framework_Error
{
    public static $enabled = true;
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Wrapper for PHP warnings.
 * You can disable notice-to-exception conversion by setting
 *
 * <code>
 * PHPUnit_Framework_Error_Warning::$enabled = false;
 * </code>
 */
class PHPUnit_Framework_Error_Warning extends PHPUnit_Framework_Error
{
    public static $enabled = true;
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Wrapper for PHP errors.
 */
class PHPUnit_Framework_Error extends PHPUnit_Framework_Exception
{
    /**
     * Constructor.
     *
     * @param string    $message
     * @param int       $code
     * @param string    $file
     * @param int       $line
     * @param Exception $previous
     */
    public function __construct($message, $code, $file, $line, Exception $previous = null)
    {
        parent::__construct($message, $code, $previous);

        $this->file  = $file;
        $this->line  = $line;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Base class for all PHPUnit Framework exceptions.
 *
 * Ensures that exceptions thrown during a test run do not leave stray
 * references behind.
 *
 * Every Exception contains a stack trace. Each stack frame contains the 'args'
 * of the called function. The function arguments can contain references to
 * instantiated objects. The references prevent the objects from being
 * destructed (until test results are eventually printed), so memory cannot be
 * freed up.
 *
 * With enabled process isolation, test results are serialized in the child
 * process and unserialized in the parent process. The stack trace of Exceptions
 * may contain objects that cannot be serialized or unserialized (e.g., PDO
 * connections). Unserializing user-space objects from the child process into
 * the parent would break the intended encapsulation of process isolation.
 *
 * @see http://fabien.potencier.org/article/9/php-serialization-stack-traces-and-exceptions
 */
class PHPUnit_Framework_Exception extends RuntimeException implements PHPUnit_Exception
{
    /**
     * @var array
     */
    protected $serializableTrace;

    public function __construct($message = '', $code = 0, Exception $previous = null)
    {
        parent::__construct($message, $code, $previous);

        $this->serializableTrace = $this->getTrace();
        foreach ($this->serializableTrace as $i => $call) {
            unset($this->serializableTrace[$i]['args']);
        }
    }

    /**
     * Returns the serializable trace (without 'args').
     *
     * @return array
     */
    public function getSerializableTrace()
    {
        return $this->serializableTrace;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        $string = PHPUnit_Framework_TestFailure::exceptionToString($this);

        if ($trace = PHPUnit_Util_Filter::getFilteredStacktrace($this)) {
            $string .= "\n" . $trace;
        }

        return $string;
    }

    public function __sleep()
    {
        return array_keys(get_object_vars($this));
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Wraps Exceptions thrown by code under test.
 *
 * Re-instantiates Exceptions thrown by user-space code to retain their original
 * class names, properties, and stack traces (but without arguments).
 *
 * Unlike PHPUnit_Framework_Exception, the complete stack of previous Exceptions
 * is processed.
 */
class PHPUnit_Framework_ExceptionWrapper extends PHPUnit_Framework_Exception
{
    /**
     * @var string
     */
    protected $className;

    /**
     * @var PHPUnit_Framework_ExceptionWrapper|null
     */
    protected $previous;

    /**
     * @param Throwable|Exception $e
     */
    public function __construct($e)
    {
        // PDOException::getCode() is a string.
        // @see http://php.net/manual/en/class.pdoexception.php#95812
        parent::__construct($e->getMessage(), (int) $e->getCode());

        $this->className = get_class($e);
        $this->file      = $e->getFile();
        $this->line      = $e->getLine();

        $this->serializableTrace = $e->getTrace();

        foreach ($this->serializableTrace as $i => $call) {
            unset($this->serializableTrace[$i]['args']);
        }

        if ($e->getPrevious()) {
            $this->previous = new self($e->getPrevious());
        }
    }

    /**
     * @return string
     */
    public function getClassName()
    {
        return $this->className;
    }

    /**
     * @return PHPUnit_Framework_ExceptionWrapper
     */
    public function getPreviousWrapped()
    {
        return $this->previous;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        $string = PHPUnit_Framework_TestFailure::exceptionToString($this);

        if ($trace = PHPUnit_Util_Filter::getFilteredStacktrace($this)) {
            $string .= "\n" . $trace;
        }

        if ($this->previous) {
            $string .= "\nCaused by\n" . $this->previous;
        }

        return $string;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Exception for expectations which failed their check.
 *
 * The exception contains the error message and optionally a
 * SebastianBergmann\Comparator\ComparisonFailure which is used to
 * generate diff output of the failed expectations.
 */
class PHPUnit_Framework_ExpectationFailedException extends PHPUnit_Framework_AssertionFailedError
{
    /**
     * @var SebastianBergmann\Comparator\ComparisonFailure
     */
    protected $comparisonFailure;

    public function __construct($message, SebastianBergmann\Comparator\ComparisonFailure $comparisonFailure = null, Exception $previous = null)
    {
        $this->comparisonFailure = $comparisonFailure;

        parent::__construct($message, 0, $previous);
    }

    /**
     * @return SebastianBergmann\Comparator\ComparisonFailure
     */
    public function getComparisonFailure()
    {
        return $this->comparisonFailure;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A marker interface for marking any exception/error as result of an unit
 * test as incomplete implementation or currently not implemented.
 */
interface PHPUnit_Framework_IncompleteTest
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * An incomplete test case
 */
class PHPUnit_Framework_IncompleteTestCase extends PHPUnit_Framework_TestCase
{
    /**
     * @var string
     */
    protected $message = '';

    /**
     * @var bool
     */
    protected $backupGlobals = false;

    /**
     * @var bool
     */
    protected $backupStaticAttributes = false;

    /**
     * @var bool
     */
    protected $runTestInSeparateProcess = false;

    /**
     * @var bool
     */
    protected $useErrorHandler = false;

    /**
     * @var bool
     */
    protected $useOutputBuffering = false;

    /**
     * @param string $className
     * @param string $methodName
     * @param string $message
     */
    public function __construct($className, $methodName, $message = '')
    {
        $this->message = $message;
        parent::__construct($className . '::' . $methodName);
    }

    /**
     * @throws PHPUnit_Framework_Exception
     */
    protected function runTest()
    {
        $this->markTestIncomplete($this->message);
    }

    /**
     * @return string
     */
    public function getMessage()
    {
        return $this->message;
    }

    /**
     * Returns a string representation of the test case.
     *
     * @return string
     */
    public function toString()
    {
        return $this->getName();
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Extension to PHPUnit_Framework_AssertionFailedError to mark the special
 * case of an incomplete test.
 */
class PHPUnit_Framework_IncompleteTestError extends PHPUnit_Framework_AssertionFailedError implements PHPUnit_Framework_IncompleteTest
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Framework_InvalidCoversTargetException extends PHPUnit_Framework_CodeCoverageException
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Extension to PHPUnit_Framework_AssertionFailedError to mark a test as risky
 * when it does not have a @covers annotation but is expected to have one.
 */
class PHPUnit_Framework_MissingCoversAnnotationException extends PHPUnit_Framework_RiskyTestError
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Extension to PHPUnit_Framework_AssertionFailedError to mark the special
 * case of a test that printed output.
 */
class PHPUnit_Framework_OutputError extends PHPUnit_Framework_AssertionFailedError
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A marker interface for marking any exception/error as result of an unit
 * test as risky.
 */
interface PHPUnit_Framework_RiskyTest
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Extension to PHPUnit_Framework_AssertionFailedError to mark the special
 * case of a risky test.
 */
class PHPUnit_Framework_RiskyTestError extends PHPUnit_Framework_AssertionFailedError implements PHPUnit_Framework_RiskyTest
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Interface for classes that can return a description of itself.
 */
interface PHPUnit_Framework_SelfDescribing
{
    /**
     * Returns a string representation of the object.
     *
     * @return string
     */
    public function toString();
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A marker interface for marking a unit test as being skipped.
 */
interface PHPUnit_Framework_SkippedTest
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A skipped test case
 */
class PHPUnit_Framework_SkippedTestCase extends PHPUnit_Framework_TestCase
{
    /**
     * @var string
     */
    protected $message = '';

    /**
     * @var bool
     */
    protected $backupGlobals = false;

    /**
     * @var bool
     */
    protected $backupStaticAttributes = false;

    /**
     * @var bool
     */
    protected $runTestInSeparateProcess = false;

    /**
     * @var bool
     */
    protected $useErrorHandler = false;

    /**
     * @var bool
     */
    protected $useOutputBuffering = false;

    /**
     * @param string $message
     */
    public function __construct($className, $methodName, $message = '')
    {
        $this->message = $message;
        parent::__construct($className . '::' . $methodName);
    }

    /**
     * @throws PHPUnit_Framework_Exception
     */
    protected function runTest()
    {
        $this->markTestSkipped($this->message);
    }

    /**
     * @return string
     */
    public function getMessage()
    {
        return $this->message;
    }

    /**
     * Returns a string representation of the test case.
     *
     * @return string
     */
    public function toString()
    {
        return $this->getName();
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Extension to PHPUnit_Framework_AssertionFailedError to mark the special
 * case of a skipped test.
 */
class PHPUnit_Framework_SkippedTestError extends PHPUnit_Framework_AssertionFailedError implements PHPUnit_Framework_SkippedTest
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Extension to PHPUnit_Framework_AssertionFailedError to mark the special
 * case of a skipped test suite.
 */
class PHPUnit_Framework_SkippedTestSuiteError extends PHPUnit_Framework_AssertionFailedError implements PHPUnit_Framework_SkippedTest
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Creates a synthetic failed assertion.
 */
class PHPUnit_Framework_SyntheticError extends PHPUnit_Framework_AssertionFailedError
{
    /**
     * The synthetic file.
     *
     * @var string
     */
    protected $syntheticFile = '';

    /**
     * The synthetic line number.
     *
     * @var int
     */
    protected $syntheticLine = 0;

    /**
     * The synthetic trace.
     *
     * @var array
     */
    protected $syntheticTrace = [];

    /**
     * Constructor.
     *
     * @param string $message
     * @param int    $code
     * @param string $file
     * @param int    $line
     * @param array  $trace
     */
    public function __construct($message, $code, $file, $line, $trace)
    {
        parent::__construct($message, $code);

        $this->syntheticFile  = $file;
        $this->syntheticLine  = $line;
        $this->syntheticTrace = $trace;
    }

    /**
     * @return string
     */
    public function getSyntheticFile()
    {
        return $this->syntheticFile;
    }

    /**
     * @return int
     */
    public function getSyntheticLine()
    {
        return $this->syntheticLine;
    }

    /**
     * @return array
     */
    public function getSyntheticTrace()
    {
        return $this->syntheticTrace;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A Test can be run and collect its results.
 */
interface PHPUnit_Framework_Test extends Countable
{
    /**
     * Runs a test and collects its result in a TestResult instance.
     *
     * @param PHPUnit_Framework_TestResult $result
     *
     * @return PHPUnit_Framework_TestResult
     */
    public function run(PHPUnit_Framework_TestResult $result = null);
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use SebastianBergmann\GlobalState\Snapshot;
use SebastianBergmann\GlobalState\Restorer;
use SebastianBergmann\GlobalState\Blacklist;
use SebastianBergmann\Diff\Differ;
use SebastianBergmann\Exporter\Exporter;
use SebastianBergmann\ObjectEnumerator\Enumerator;
use Prophecy\Exception\Prediction\PredictionException;
use Prophecy\Prophet;
use DeepCopy\DeepCopy;

/**
 * A TestCase defines the fixture to run multiple tests.
 *
 * To define a TestCase
 *
 *   1) Implement a subclass of PHPUnit_Framework_TestCase.
 *   2) Define instance variables that store the state of the fixture.
 *   3) Initialize the fixture state by overriding setUp().
 *   4) Clean-up after a test by overriding tearDown().
 *
 * Each test runs in its own fixture so there can be no side effects
 * among test runs.
 *
 * Here is an example:
 *
 * <code>
 * <?php
 * class MathTest extends PHPUnit_Framework_TestCase
 * {
 *     public $value1;
 *     public $value2;
 *
 *     protected function setUp()
 *     {
 *         $this->value1 = 2;
 *         $this->value2 = 3;
 *     }
 * }
 * ?>
 * </code>
 *
 * For each test implement a method which interacts with the fixture.
 * Verify the expected results with assertions specified by calling
 * assert with a boolean.
 *
 * <code>
 * <?php
 * public function testPass()
 * {
 *     $this->assertTrue($this->value1 + $this->value2 == 5);
 * }
 * ?>
 * </code>
 */
abstract class PHPUnit_Framework_TestCase extends PHPUnit_Framework_Assert implements PHPUnit_Framework_Test, PHPUnit_Framework_SelfDescribing
{
    /**
     * Enable or disable the backup and restoration of the $GLOBALS array.
     * Overwrite this attribute in a child class of TestCase.
     * Setting this attribute in setUp() has no effect!
     *
     * @var bool
     */
    protected $backupGlobals = null;

    /**
     * @var array
     */
    protected $backupGlobalsBlacklist = [];

    /**
     * Enable or disable the backup and restoration of static attributes.
     * Overwrite this attribute in a child class of TestCase.
     * Setting this attribute in setUp() has no effect!
     *
     * @var bool
     */
    protected $backupStaticAttributes = null;

    /**
     * @var array
     */
    protected $backupStaticAttributesBlacklist = [];

    /**
     * Whether or not this test is to be run in a separate PHP process.
     *
     * @var bool
     */
    protected $runTestInSeparateProcess = null;

    /**
     * Whether or not this test should preserve the global state when
     * running in a separate PHP process.
     *
     * @var bool
     */
    protected $preserveGlobalState = true;

    /**
     * Whether or not this test is running in a separate PHP process.
     *
     * @var bool
     */
    private $inIsolation = false;

    /**
     * @var array
     */
    private $data = [];

    /**
     * @var string
     */
    private $dataName = '';

    /**
     * @var bool
     */
    private $useErrorHandler = null;

    /**
     * The name of the expected Exception.
     *
     * @var string
     */
    private $expectedException = null;

    /**
     * The message of the expected Exception.
     *
     * @var string
     */
    private $expectedExceptionMessage = '';

    /**
     * The regex pattern to validate the expected Exception message.
     *
     * @var string
     */
    private $expectedExceptionMessageRegExp = '';

    /**
     * The code of the expected Exception.
     *
     * @var int|string
     */
    private $expectedExceptionCode;

    /**
     * The name of the test case.
     *
     * @var string
     */
    private $name = null;

    /**
     * @var array
     */
    private $dependencies = [];

    /**
     * @var array
     */
    private $dependencyInput = [];

    /**
     * @var array
     */
    private $iniSettings = [];

    /**
     * @var array
     */
    private $locale = [];

    /**
     * @var array
     */
    private $mockObjects = [];

    /**
     * @var array
     */
    private $mockObjectGenerator = null;

    /**
     * @var int
     */
    private $status;

    /**
     * @var string
     */
    private $statusMessage = '';

    /**
     * @var int
     */
    private $numAssertions = 0;

    /**
     * @var PHPUnit_Framework_TestResult
     */
    private $result;

    /**
     * @var mixed
     */
    private $testResult;

    /**
     * @var string
     */
    private $output = '';

    /**
     * @var string
     */
    private $outputExpectedRegex = null;

    /**
     * @var string
     */
    private $outputExpectedString = null;

    /**
     * @var mixed
     */
    private $outputCallback = false;

    /**
     * @var bool
     */
    private $outputBufferingActive = false;

    /**
     * @var int
     */
    private $outputBufferingLevel;

    /**
     * @var SebastianBergmann\GlobalState\Snapshot
     */
    private $snapshot;

    /**
     * @var Prophecy\Prophet
     */
    private $prophet;

    /**
     * @var bool
     */
    private $beStrictAboutChangesToGlobalState = false;

    /**
     * @var bool
     */
    private $registerMockObjectsFromTestArgumentsRecursively = false;

    /**
     * @var string[]
     */
    private $warnings = [];

    /**
     * @var array
     */
    private $groups = [];

    /**
     * @var bool
     */
    private $doesNotPerformAssertions = false;

    /**
     * Constructs a test case with the given name.
     *
     * @param string $name
     * @param array  $data
     * @param string $dataName
     */
    public function __construct($name = null, array $data = [], $dataName = '')
    {
        if ($name !== null) {
            $this->setName($name);
        }

        $this->data     = $data;
        $this->dataName = $dataName;
    }

    /**
     * Returns a string representation of the test case.
     *
     * @return string
     */
    public function toString()
    {
        $class = new ReflectionClass($this);

        $buffer = sprintf(
            '%s::%s',
            $class->name,
            $this->getName(false)
        );

        return $buffer . $this->getDataSetAsString();
    }

    /**
     * Counts the number of test cases executed by run(TestResult result).
     *
     * @return int
     */
    public function count()
    {
        return 1;
    }

    public function getGroups()
    {
        return $this->groups;
    }

    /**
     * @param array $groups
     */
    public function setGroups(array $groups)
    {
        $this->groups = $groups;
    }

    /**
     * Returns the annotations for this test.
     *
     * @return array
     */
    public function getAnnotations()
    {
        return PHPUnit_Util_Test::parseTestMethodAnnotations(
            get_class($this),
            $this->name
        );
    }

    /**
     * Gets the name of a TestCase.
     *
     * @param bool $withDataSet
     *
     * @return string
     */
    public function getName($withDataSet = true)
    {
        if ($withDataSet) {
            return $this->name . $this->getDataSetAsString(false);
        } else {
            return $this->name;
        }
    }

    /**
     * Returns the size of the test.
     *
     * @return int
     */
    public function getSize()
    {
        return PHPUnit_Util_Test::getSize(
            get_class($this),
            $this->getName(false)
        );
    }

    /**
     * @return bool
     */
    public function hasSize()
    {
        return $this->getSize() !== PHPUnit_Util_Test::UNKNOWN;
    }

    /**
     * @return bool
     */
    public function isSmall()
    {
        return $this->getSize() === PHPUnit_Util_Test::SMALL;
    }

    /**
     * @return bool
     */
    public function isMedium()
    {
        return $this->getSize() === PHPUnit_Util_Test::MEDIUM;
    }

    /**
     * @return bool
     */
    public function isLarge()
    {
        return $this->getSize() === PHPUnit_Util_Test::LARGE;
    }

    /**
     * @return string
     */
    public function getActualOutput()
    {
        if (!$this->outputBufferingActive) {
            return $this->output;
        } else {
            return ob_get_contents();
        }
    }

    /**
     * @return bool
     */
    public function hasOutput()
    {
        if (strlen($this->output) === 0) {
            return false;
        }

        if ($this->hasExpectationOnOutput()) {
            return false;
        }

        return true;
    }

    /**
     * @return bool
     */
    public function doesNotPerformAssertions()
    {
        return $this->doesNotPerformAssertions;
    }

    /**
     * @param string $expectedRegex
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function expectOutputRegex($expectedRegex)
    {
        if ($this->outputExpectedString !== null) {
            throw new PHPUnit_Framework_Exception;
        }

        if (is_string($expectedRegex) || is_null($expectedRegex)) {
            $this->outputExpectedRegex = $expectedRegex;
        }
    }

    /**
     * @param string $expectedString
     */
    public function expectOutputString($expectedString)
    {
        if ($this->outputExpectedRegex !== null) {
            throw new PHPUnit_Framework_Exception;
        }

        if (is_string($expectedString) || is_null($expectedString)) {
            $this->outputExpectedString = $expectedString;
        }
    }

    /**
     * @return bool
     *
     * @deprecated Use hasExpectationOnOutput() instead
     */
    public function hasPerformedExpectationsOnOutput()
    {
        return $this->hasExpectationOnOutput();
    }

    /**
     * @return bool
     */
    public function hasExpectationOnOutput()
    {
        return is_string($this->outputExpectedString) || is_string($this->outputExpectedRegex);
    }

    /**
     * @return string
     */
    public function getExpectedException()
    {
        return $this->expectedException;
    }

    /**
     * @param mixed      $exception
     * @param string     $message
     * @param int|string $code
     *
     * @throws PHPUnit_Framework_Exception
     *
     * @deprecated Method deprecated since Release 5.2.0; use expectException() instead
     */
    public function setExpectedException($exception, $message = '', $code = null)
    {
        $this->expectedException = $exception;

        if ($message !== null && $message !== '') {
            $this->expectExceptionMessage($message);
        }

        if ($code !== null) {
            $this->expectExceptionCode($code);
        }
    }

    /**
     * @param mixed  $exception
     * @param string $messageRegExp
     * @param int    $code
     *
     * @throws PHPUnit_Framework_Exception
     *
     * @deprecated Method deprecated since Release 5.6.0; use expectExceptionMessageRegExp() instead
     */
    public function setExpectedExceptionRegExp($exception, $messageRegExp = '', $code = null)
    {
        if (!is_string($messageRegExp)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
        }

        $this->expectedException              = $exception;
        $this->expectedExceptionMessageRegExp = $messageRegExp;

        if ($code !== null) {
            $this->expectExceptionCode($code);
        }
    }

    /**
     * @param string $exception
     */
    public function expectException($exception)
    {
        if (!is_string($exception)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        $this->expectedException = $exception;
    }

    /**
     * @param int|string $code
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function expectExceptionCode($code)
    {
        if (!$this->expectedException) {
            $this->expectedException = \Exception::class;
        }

        if (!is_int($code) && !is_string($code)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer or string');
        }

        $this->expectedExceptionCode = $code;
    }

    /**
     * @param string $message
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function expectExceptionMessage($message)
    {
        if (!$this->expectedException) {
            $this->expectedException = \Exception::class;
        }

        if (!is_string($message)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        $this->expectedExceptionMessage = $message;
    }

    /**
     * @param string $messageRegExp
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function expectExceptionMessageRegExp($messageRegExp)
    {
        if (!is_string($messageRegExp)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        $this->expectedExceptionMessageRegExp = $messageRegExp;
    }

    /**
     * @param bool $flag
     */
    public function setRegisterMockObjectsFromTestArgumentsRecursively($flag)
    {
        if (!is_bool($flag)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }

        $this->registerMockObjectsFromTestArgumentsRecursively = $flag;
    }

    protected function setExpectedExceptionFromAnnotation()
    {
        try {
            $expectedException = PHPUnit_Util_Test::getExpectedException(
                get_class($this),
                $this->name
            );

            if ($expectedException !== false) {
                $this->expectException($expectedException['class']);

                if ($expectedException['code'] !== null) {
                    $this->expectExceptionCode($expectedException['code']);
                }

                if ($expectedException['message'] !== '') {
                    $this->expectExceptionMessage($expectedException['message']);
                } elseif ($expectedException['message_regex'] !== '') {
                    $this->expectExceptionMessageRegExp($expectedException['message_regex']);
                }
            }
        } catch (ReflectionException $e) {
        }
    }

    /**
     * @param bool $useErrorHandler
     */
    public function setUseErrorHandler($useErrorHandler)
    {
        $this->useErrorHandler = $useErrorHandler;
    }

    protected function setUseErrorHandlerFromAnnotation()
    {
        try {
            $useErrorHandler = PHPUnit_Util_Test::getErrorHandlerSettings(
                get_class($this),
                $this->name
            );

            if ($useErrorHandler !== null) {
                $this->setUseErrorHandler($useErrorHandler);
            }
        } catch (ReflectionException $e) {
        }
    }

    protected function checkRequirements()
    {
        if (!$this->name || !method_exists($this, $this->name)) {
            return;
        }

        $missingRequirements = PHPUnit_Util_Test::getMissingRequirements(
            get_class($this),
            $this->name
        );

        if (!empty($missingRequirements)) {
            $this->markTestSkipped(implode(PHP_EOL, $missingRequirements));
        }
    }

    /**
     * Returns the status of this test.
     *
     * @return int
     */
    public function getStatus()
    {
        return $this->status;
    }

    public function markAsRisky()
    {
        $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_RISKY;
    }

    /**
     * Returns the status message of this test.
     *
     * @return string
     */
    public function getStatusMessage()
    {
        return $this->statusMessage;
    }

    /**
     * Returns whether or not this test has failed.
     *
     * @return bool
     */
    public function hasFailed()
    {
        $status = $this->getStatus();

        return $status == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE ||
               $status == PHPUnit_Runner_BaseTestRunner::STATUS_ERROR;
    }

    /**
     * Runs the test case and collects the results in a TestResult object.
     * If no TestResult object is passed a new one will be created.
     *
     * @param PHPUnit_Framework_TestResult $result
     *
     * @return PHPUnit_Framework_TestResult
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function run(PHPUnit_Framework_TestResult $result = null)
    {
        if ($result === null) {
            $result = $this->createResult();
        }

        if (!$this instanceof PHPUnit_Framework_WarningTestCase) {
            $this->setTestResultObject($result);
            $this->setUseErrorHandlerFromAnnotation();
        }

        if ($this->useErrorHandler !== null) {
            $oldErrorHandlerSetting = $result->getConvertErrorsToExceptions();
            $result->convertErrorsToExceptions($this->useErrorHandler);
        }

        if (!$this instanceof PHPUnit_Framework_WarningTestCase &&
            !$this instanceof PHPUnit_Framework_SkippedTestCase &&
            !$this->handleDependencies()) {
            return;
        }

        if ($this->runTestInSeparateProcess === true &&
            $this->inIsolation !== true &&
            !$this instanceof PHPUnit_Extensions_PhptTestCase) {
            $class = new ReflectionClass($this);

            $template = new Text_Template(
                __DIR__ . '/../Util/PHP/Template/TestCaseMethod.tpl'
            );

            if ($this->preserveGlobalState) {
                $constants     = PHPUnit_Util_GlobalState::getConstantsAsString();
                $globals       = PHPUnit_Util_GlobalState::getGlobalsAsString();
                $includedFiles = PHPUnit_Util_GlobalState::getIncludedFilesAsString();
                $iniSettings   = PHPUnit_Util_GlobalState::getIniSettingsAsString();
            } else {
                $constants     = '';
                if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) {
                    $globals     = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . var_export($GLOBALS['__PHPUNIT_BOOTSTRAP'], true) . ";\n";
                } else {
                    $globals     = '';
                }
                $includedFiles = '';
                $iniSettings   = '';
            }

            $coverage                                   = $result->getCollectCodeCoverageInformation()          ? 'true' : 'false';
            $isStrictAboutTestsThatDoNotTestAnything    = $result->isStrictAboutTestsThatDoNotTestAnything()    ? 'true' : 'false';
            $isStrictAboutOutputDuringTests             = $result->isStrictAboutOutputDuringTests()             ? 'true' : 'false';
            $enforcesTimeLimit                          = $result->enforcesTimeLimit()                          ? 'true' : 'false';
            $isStrictAboutTodoAnnotatedTests            = $result->isStrictAboutTodoAnnotatedTests()            ? 'true' : 'false';
            $isStrictAboutResourceUsageDuringSmallTests = $result->isStrictAboutResourceUsageDuringSmallTests() ? 'true' : 'false';

            if (defined('PHPUNIT_COMPOSER_INSTALL')) {
                $composerAutoload = var_export(PHPUNIT_COMPOSER_INSTALL, true);
            } else {
                $composerAutoload = '\'\'';
            }

            if (defined('__PHPUNIT_PHAR__')) {
                $phar = var_export(__PHPUNIT_PHAR__, true);
            } else {
                $phar = '\'\'';
            }

            if ($result->getCodeCoverage()) {
                $codeCoverageFilter = $result->getCodeCoverage()->filter();
            } else {
                $codeCoverageFilter = null;
            }

            $data               = var_export(serialize($this->data), true);
            $dataName           = var_export($this->dataName, true);
            $dependencyInput    = var_export(serialize($this->dependencyInput), true);
            $includePath        = var_export(get_include_path(), true);
            $codeCoverageFilter = var_export(serialize($codeCoverageFilter), true);
            // must do these fixes because TestCaseMethod.tpl has unserialize('{data}') in it, and we can't break BC
            // the lines above used to use addcslashes() rather than var_export(), which breaks null byte escape sequences
            $data               = "'." . $data . ".'";
            $dataName           = "'.(" . $dataName . ").'";
            $dependencyInput    = "'." . $dependencyInput . ".'";
            $includePath        = "'." . $includePath . ".'";
            $codeCoverageFilter = "'." . $codeCoverageFilter . ".'";

            $configurationFilePath = (isset($GLOBALS['__PHPUNIT_CONFIGURATION_FILE']) ? $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] : '');

            $template->setVar(
                [
                    'composerAutoload'                           => $composerAutoload,
                    'phar'                                       => $phar,
                    'filename'                                   => $class->getFileName(),
                    'className'                                  => $class->getName(),
                    'methodName'                                 => $this->name,
                    'collectCodeCoverageInformation'             => $coverage,
                    'data'                                       => $data,
                    'dataName'                                   => $dataName,
                    'dependencyInput'                            => $dependencyInput,
                    'constants'                                  => $constants,
                    'globals'                                    => $globals,
                    'include_path'                               => $includePath,
                    'included_files'                             => $includedFiles,
                    'iniSettings'                                => $iniSettings,
                    'isStrictAboutTestsThatDoNotTestAnything'    => $isStrictAboutTestsThatDoNotTestAnything,
                    'isStrictAboutOutputDuringTests'             => $isStrictAboutOutputDuringTests,
                    'enforcesTimeLimit'                          => $enforcesTimeLimit,
                    'isStrictAboutTodoAnnotatedTests'            => $isStrictAboutTodoAnnotatedTests,
                    'isStrictAboutResourceUsageDuringSmallTests' => $isStrictAboutResourceUsageDuringSmallTests,
                    'codeCoverageFilter'                         => $codeCoverageFilter,
                    'configurationFilePath'                      => $configurationFilePath
                ]
            );

            $this->prepareTemplate($template);

            $php = PHPUnit_Util_PHP::factory();
            $php->runTestJob($template->render(), $this, $result);
        } else {
            $result->run($this);
        }

        if ($this->useErrorHandler !== null) {
            $result->convertErrorsToExceptions($oldErrorHandlerSetting);
        }

        $this->result = null;

        return $result;
    }

    /**
     * Runs the bare test sequence.
     */
    public function runBare()
    {
        $this->numAssertions = 0;

        $this->snapshotGlobalState();
        $this->startOutputBuffering();
        clearstatcache();
        $currentWorkingDirectory = getcwd();

        $hookMethods = PHPUnit_Util_Test::getHookMethods(get_class($this));

        try {
            $hasMetRequirements = false;
            $this->checkRequirements();
            $hasMetRequirements = true;

            if ($this->inIsolation) {
                foreach ($hookMethods['beforeClass'] as $method) {
                    $this->$method();
                }
            }

            $this->setExpectedExceptionFromAnnotation();
            $this->setDoesNotPerformAssertionsFromAnnotation();

            foreach ($hookMethods['before'] as $method) {
                $this->$method();
            }

            $this->assertPreConditions();
            $this->testResult = $this->runTest();
            $this->verifyMockObjects();
            $this->assertPostConditions();

            if (!empty($this->warnings)) {
                throw new PHPUnit_Framework_Warning(
                    implode(
                        "\n",
                        array_unique($this->warnings)
                    )
                );
            }

            $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_PASSED;
        } catch (PHPUnit_Framework_IncompleteTest $e) {
            $this->status        = PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE;
            $this->statusMessage = $e->getMessage();
        } catch (PHPUnit_Framework_SkippedTest $e) {
            $this->status        = PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED;
            $this->statusMessage = $e->getMessage();
        } catch (PHPUnit_Framework_Warning $e) {
            $this->status        = PHPUnit_Runner_BaseTestRunner::STATUS_WARNING;
            $this->statusMessage = $e->getMessage();
        } catch (PHPUnit_Framework_AssertionFailedError $e) {
            $this->status        = PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE;
            $this->statusMessage = $e->getMessage();
        } catch (PredictionException $e) {
            $this->status        = PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE;
            $this->statusMessage = $e->getMessage();
        } catch (Throwable $_e) {
            $e = $_e;
        } catch (Exception $_e) {
            $e = $_e;
        }

        if (isset($_e)) {
            $this->status        = PHPUnit_Runner_BaseTestRunner::STATUS_ERROR;
            $this->statusMessage = $_e->getMessage();
        }

        // Clean up the mock objects.
        $this->mockObjects = [];
        $this->prophet     = null;

        // Tear down the fixture. An exception raised in tearDown() will be
        // caught and passed on when no exception was raised before.
        try {
            if ($hasMetRequirements) {
                foreach ($hookMethods['after'] as $method) {
                    $this->$method();
                }

                if ($this->inIsolation) {
                    foreach ($hookMethods['afterClass'] as $method) {
                        $this->$method();
                    }
                }
            }
        } catch (Throwable $_e) {
            if (!isset($e)) {
                $e = $_e;
            }
        } catch (Exception $_e) {
            if (!isset($e)) {
                $e = $_e;
            }
        }

        try {
            $this->stopOutputBuffering();
        } catch (PHPUnit_Framework_RiskyTestError $_e) {
            if (!isset($e)) {
                $e = $_e;
            }
        }

        clearstatcache();

        if ($currentWorkingDirectory != getcwd()) {
            chdir($currentWorkingDirectory);
        }

        $this->restoreGlobalState();

        // Clean up INI settings.
        foreach ($this->iniSettings as $varName => $oldValue) {
            ini_set($varName, $oldValue);
        }

        $this->iniSettings = [];

        // Clean up locale settings.
        foreach ($this->locale as $category => $locale) {
            setlocale($category, $locale);
        }

        // Perform assertion on output.
        if (!isset($e)) {
            try {
                if ($this->outputExpectedRegex !== null) {
                    $this->assertRegExp($this->outputExpectedRegex, $this->output);
                } elseif ($this->outputExpectedString !== null) {
                    $this->assertEquals($this->outputExpectedString, $this->output);
                }
            } catch (Throwable $_e) {
                $e = $_e;
            } catch (Exception $_e) {
                $e = $_e;
            }
        }

        // Workaround for missing "finally".
        if (isset($e)) {
            if ($e instanceof PredictionException) {
                $e = new PHPUnit_Framework_AssertionFailedError($e->getMessage());
            }

            $this->onNotSuccessfulTest($e);
        }
    }

    /**
     * Override to run the test and assert its state.
     *
     * @return mixed
     *
     * @throws Exception|PHPUnit_Framework_Exception
     * @throws PHPUnit_Framework_Exception
     */
    protected function runTest()
    {
        if ($this->name === null) {
            throw new PHPUnit_Framework_Exception(
                'PHPUnit_Framework_TestCase::$name must not be null.'
            );
        }

        try {
            $class  = new ReflectionClass($this);
            $method = $class->getMethod($this->name);
        } catch (ReflectionException $e) {
            $this->fail($e->getMessage());
        }

        $testArguments = array_merge($this->data, $this->dependencyInput);

        $this->registerMockObjectsFromTestArguments($testArguments);

        try {
            $testResult = $method->invokeArgs($this, $testArguments);
        } catch (Throwable $_e) {
            $e = $_e;
        } catch (Exception $_e) {
            $e = $_e;
        }

        if (isset($e)) {
            $checkException = false;

            if (!($e instanceof PHPUnit_Framework_SkippedTestError) && is_string($this->expectedException)) {
                $checkException = true;

                if ($e instanceof PHPUnit_Framework_Exception) {
                    $checkException = false;
                }

                $reflector = new ReflectionClass($this->expectedException);

                if ($this->expectedException === 'PHPUnit_Framework_Exception' ||
                    $this->expectedException === '\PHPUnit_Framework_Exception' ||
                    $reflector->isSubclassOf('PHPUnit_Framework_Exception')) {
                    $checkException = true;
                }
            }

            if ($checkException) {
                $this->assertThat(
                    $e,
                    new PHPUnit_Framework_Constraint_Exception(
                        $this->expectedException
                    )
                );

                if (is_string($this->expectedExceptionMessage) &&
                    !empty($this->expectedExceptionMessage)) {
                    $this->assertThat(
                        $e,
                        new PHPUnit_Framework_Constraint_ExceptionMessage(
                            $this->expectedExceptionMessage
                        )
                    );
                }

                if (is_string($this->expectedExceptionMessageRegExp) &&
                    !empty($this->expectedExceptionMessageRegExp)) {
                    $this->assertThat(
                        $e,
                        new PHPUnit_Framework_Constraint_ExceptionMessageRegExp(
                            $this->expectedExceptionMessageRegExp
                        )
                    );
                }

                if ($this->expectedExceptionCode !== null) {
                    $this->assertThat(
                        $e,
                        new PHPUnit_Framework_Constraint_ExceptionCode(
                            $this->expectedExceptionCode
                        )
                    );
                }

                return;
            } else {
                throw $e;
            }
        }

        if ($this->expectedException !== null) {
            $this->assertThat(
                null,
                new PHPUnit_Framework_Constraint_Exception(
                    $this->expectedException
                )
            );
        }

        return $testResult;
    }

    /**
     * Verifies the mock object expectations.
     */
    protected function verifyMockObjects()
    {
        foreach ($this->mockObjects as $mockObject) {
            if ($mockObject->__phpunit_hasMatchers()) {
                $this->numAssertions++;
            }

            $mockObject->__phpunit_verify(
                $this->shouldInvocationMockerBeReset($mockObject)
            );
        }

        if ($this->prophet !== null) {
            try {
                $this->prophet->checkPredictions();
            } catch (Throwable $t) {
                /* Intentionally left empty */
            } catch (Exception $t) {
                /* Intentionally left empty */
            }

            foreach ($this->prophet->getProphecies() as $objectProphecy) {
                foreach ($objectProphecy->getMethodProphecies() as $methodProphecies) {
                    foreach ($methodProphecies as $methodProphecy) {
                        $this->numAssertions += count($methodProphecy->getCheckedPredictions());
                    }
                }
            }

            if (isset($t)) {
                throw $t;
            }
        }
    }

    /**
     * Sets the name of a TestCase.
     *
     * @param  string
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Sets the dependencies of a TestCase.
     *
     * @param array $dependencies
     */
    public function setDependencies(array $dependencies)
    {
        $this->dependencies = $dependencies;
    }

    /**
     * Returns true if the tests has dependencies
     *
     * @return bool
     */
    public function hasDependencies()
    {
        return count($this->dependencies) > 0;
    }

    /**
     * Sets
     *
     * @param array $dependencyInput
     */
    public function setDependencyInput(array $dependencyInput)
    {
        $this->dependencyInput = $dependencyInput;
    }

    /**
     * @param bool $beStrictAboutChangesToGlobalState
     */
    public function setBeStrictAboutChangesToGlobalState($beStrictAboutChangesToGlobalState)
    {
        $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState;
    }

    /**
     * Calling this method in setUp() has no effect!
     *
     * @param bool $backupGlobals
     */
    public function setBackupGlobals($backupGlobals)
    {
        if (is_null($this->backupGlobals) && is_bool($backupGlobals)) {
            $this->backupGlobals = $backupGlobals;
        }
    }

    /**
     * Calling this method in setUp() has no effect!
     *
     * @param bool $backupStaticAttributes
     */
    public function setBackupStaticAttributes($backupStaticAttributes)
    {
        if (is_null($this->backupStaticAttributes) &&
            is_bool($backupStaticAttributes)) {
            $this->backupStaticAttributes = $backupStaticAttributes;
        }
    }

    /**
     * @param bool $runTestInSeparateProcess
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function setRunTestInSeparateProcess($runTestInSeparateProcess)
    {
        if (is_bool($runTestInSeparateProcess)) {
            if ($this->runTestInSeparateProcess === null) {
                $this->runTestInSeparateProcess = $runTestInSeparateProcess;
            }
        } else {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }
    }

    /**
     * @param bool $preserveGlobalState
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function setPreserveGlobalState($preserveGlobalState)
    {
        if (is_bool($preserveGlobalState)) {
            $this->preserveGlobalState = $preserveGlobalState;
        } else {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }
    }

    /**
     * @param bool $inIsolation
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function setInIsolation($inIsolation)
    {
        if (is_bool($inIsolation)) {
            $this->inIsolation = $inIsolation;
        } else {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }
    }

    /**
     * @return bool
     */
    public function isInIsolation()
    {
        return $this->inIsolation;
    }

    /**
     * @return mixed
     */
    public function getResult()
    {
        return $this->testResult;
    }

    /**
     * @param mixed $result
     */
    public function setResult($result)
    {
        $this->testResult = $result;
    }

    /**
     * @param callable $callback
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function setOutputCallback($callback)
    {
        if (!is_callable($callback)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'callback');
        }

        $this->outputCallback = $callback;
    }

    /**
     * @return PHPUnit_Framework_TestResult
     */
    public function getTestResultObject()
    {
        return $this->result;
    }

    /**
     * @param PHPUnit_Framework_TestResult $result
     */
    public function setTestResultObject(PHPUnit_Framework_TestResult $result)
    {
        $this->result = $result;
    }

    /**
     * @param PHPUnit_Framework_MockObject_MockObject $mockObject
     */
    public function registerMockObject(PHPUnit_Framework_MockObject_MockObject $mockObject)
    {
        $this->mockObjects[] = $mockObject;
    }

    /**
     * This method is a wrapper for the ini_set() function that automatically
     * resets the modified php.ini setting to its original value after the
     * test is run.
     *
     * @param string $varName
     * @param string $newValue
     *
     * @throws PHPUnit_Framework_Exception
     */
    protected function iniSet($varName, $newValue)
    {
        if (!is_string($varName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        $currentValue = ini_set($varName, $newValue);

        if ($currentValue !== false) {
            $this->iniSettings[$varName] = $currentValue;
        } else {
            throw new PHPUnit_Framework_Exception(
                sprintf(
                    'INI setting "%s" could not be set to "%s".',
                    $varName,
                    $newValue
                )
            );
        }
    }

    /**
     * This method is a wrapper for the setlocale() function that automatically
     * resets the locale to its original value after the test is run.
     *
     * @param int    $category
     * @param string $locale
     *
     * @throws PHPUnit_Framework_Exception
     */
    protected function setLocale()
    {
        $args = func_get_args();

        if (count($args) < 2) {
            throw new PHPUnit_Framework_Exception;
        }

        $category = $args[0];
        $locale   = $args[1];

        $categories = [
            LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME
        ];

        if (defined('LC_MESSAGES')) {
            $categories[] = LC_MESSAGES;
        }

        if (!in_array($category, $categories)) {
            throw new PHPUnit_Framework_Exception;
        }

        if (!is_array($locale) && !is_string($locale)) {
            throw new PHPUnit_Framework_Exception;
        }

        $this->locale[$category] = setlocale($category, 0);

        $result = call_user_func_array('setlocale', $args);

        if ($result === false) {
            throw new PHPUnit_Framework_Exception(
                'The locale functionality is not implemented on your platform, ' .
                'the specified locale does not exist or the category name is ' .
                'invalid.'
            );
        }
    }

    /**
     * Returns a builder object to create mock objects using a fluent interface.
     *
     * @param string $className
     *
     * @return PHPUnit_Framework_MockObject_MockBuilder
     */
    public function getMockBuilder($className)
    {
        return new PHPUnit_Framework_MockObject_MockBuilder($this, $className);
    }

    /**
     * Returns a test double for the specified class.
     *
     * @param string $originalClassName
     *
     * @return PHPUnit_Framework_MockObject_MockObject
     *
     * @throws PHPUnit_Framework_Exception
     */
    protected function createMock($originalClassName)
    {
        return $this->getMockBuilder($originalClassName)
                    ->disableOriginalConstructor()
                    ->disableOriginalClone()
                    ->disableArgumentCloning()
                    ->disallowMockingUnknownTypes()
                    ->getMock();
    }

    /**
     * Returns a configured test double for the specified class.
     *
     * @param string $originalClassName
     * @param array  $configuration
     *
     * @return PHPUnit_Framework_MockObject_MockObject
     *
     * @throws PHPUnit_Framework_Exception
     */
    protected function createConfiguredMock($originalClassName, array $configuration)
    {
        $o = $this->createMock($originalClassName);

        foreach ($configuration as $method => $return) {
            $o->method($method)->willReturn($return);
        }

        return $o;
    }

    /**
     * Returns a partial test double for the specified class.
     *
     * @param string $originalClassName
     * @param array  $methods
     *
     * @return PHPUnit_Framework_MockObject_MockObject
     *
     * @throws PHPUnit_Framework_Exception
     */
    protected function createPartialMock($originalClassName, array $methods)
    {
        return $this->getMockBuilder($originalClassName)
                    ->disableOriginalConstructor()
                    ->disableOriginalClone()
                    ->disableArgumentCloning()
                    ->disallowMockingUnknownTypes()
                    ->setMethods(empty($methods) ? null : $methods)
                    ->getMock();
    }

    /**
     * Returns a mock object for the specified class.
     *
     * @param string     $originalClassName       Name of the class to mock.
     * @param array|null $methods                 When provided, only methods whose names are in the array
     *                                            are replaced with a configurable test double. The behavior
     *                                            of the other methods is not changed.
     *                                            Providing null means that no methods will be replaced.
     * @param array      $arguments               Parameters to pass to the original class' constructor.
     * @param string     $mockClassName           Class name for the generated test double class.
     * @param bool       $callOriginalConstructor Can be used to disable the call to the original class' constructor.
     * @param bool       $callOriginalClone       Can be used to disable the call to the original class' clone constructor.
     * @param bool       $callAutoload            Can be used to disable __autoload() during the generation of the test double class.
     * @param bool       $cloneArguments
     * @param bool       $callOriginalMethods
     * @param object     $proxyTarget
     *
     * @return PHPUnit_Framework_MockObject_MockObject
     *
     * @throws PHPUnit_Framework_Exception
     *
     * @deprecated Method deprecated since Release 5.4.0; use createMock() or getMockBuilder() instead
     */
    protected function getMock($originalClassName, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false, $callOriginalMethods = false, $proxyTarget = null)
    {
        $this->warnings[] = 'PHPUnit_Framework_TestCase::getMock() is deprecated, use PHPUnit_Framework_TestCase::createMock() or PHPUnit_Framework_TestCase::getMockBuilder() instead';

        $mockObject = $this->getMockObjectGenerator()->getMock(
            $originalClassName,
            $methods,
            $arguments,
            $mockClassName,
            $callOriginalConstructor,
            $callOriginalClone,
            $callAutoload,
            $cloneArguments,
            $callOriginalMethods,
            $proxyTarget
        );

        $this->registerMockObject($mockObject);

        return $mockObject;
    }

    /**
     * Returns a mock with disabled constructor object for the specified class.
     *
     * @param string $originalClassName
     *
     * @return PHPUnit_Framework_MockObject_MockObject
     *
     * @throws PHPUnit_Framework_Exception
     *
     * @deprecated Method deprecated since Release 5.4.0; use createMock() instead
     */
    protected function getMockWithoutInvokingTheOriginalConstructor($originalClassName)
    {
        $this->warnings[] = 'PHPUnit_Framework_TestCase::getMockWithoutInvokingTheOriginalConstructor() is deprecated, use PHPUnit_Framework_TestCase::createMock() instead';

        return $this->getMockBuilder($originalClassName)
                    ->disableOriginalConstructor()
                    ->getMock();
    }

    /**
     * Mocks the specified class and returns the name of the mocked class.
     *
     * @param string $originalClassName
     * @param array  $methods
     * @param array  $arguments
     * @param string $mockClassName
     * @param bool   $callOriginalConstructor
     * @param bool   $callOriginalClone
     * @param bool   $callAutoload
     * @param bool   $cloneArguments
     *
     * @return string
     *
     * @throws PHPUnit_Framework_Exception
     */
    protected function getMockClass($originalClassName, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = false, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false)
    {
        $mock = $this->getMockObjectGenerator()->getMock(
            $originalClassName,
            $methods,
            $arguments,
            $mockClassName,
            $callOriginalConstructor,
            $callOriginalClone,
            $callAutoload,
            $cloneArguments
        );

        return get_class($mock);
    }

    /**
     * Returns a mock object for the specified abstract class with all abstract
     * methods of the class mocked. Concrete methods are not mocked by default.
     * To mock concrete methods, use the 7th parameter ($mockedMethods).
     *
     * @param string $originalClassName
     * @param array  $arguments
     * @param string $mockClassName
     * @param bool   $callOriginalConstructor
     * @param bool   $callOriginalClone
     * @param bool   $callAutoload
     * @param array  $mockedMethods
     * @param bool   $cloneArguments
     *
     * @return PHPUnit_Framework_MockObject_MockObject
     *
     * @throws PHPUnit_Framework_Exception
     */
    protected function getMockForAbstractClass($originalClassName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false)
    {
        $mockObject = $this->getMockObjectGenerator()->getMockForAbstractClass(
            $originalClassName,
            $arguments,
            $mockClassName,
            $callOriginalConstructor,
            $callOriginalClone,
            $callAutoload,
            $mockedMethods,
            $cloneArguments
        );

        $this->registerMockObject($mockObject);

        return $mockObject;
    }

    /**
     * Returns a mock object based on the given WSDL file.
     *
     * @param string $wsdlFile
     * @param string $originalClassName
     * @param string $mockClassName
     * @param array  $methods
     * @param bool   $callOriginalConstructor
     * @param array  $options                 An array of options passed to SOAPClient::_construct
     *
     * @return PHPUnit_Framework_MockObject_MockObject
     */
    protected function getMockFromWsdl($wsdlFile, $originalClassName = '', $mockClassName = '', array $methods = [], $callOriginalConstructor = true, array $options = [])
    {
        if ($originalClassName === '') {
            $originalClassName = pathinfo(basename(parse_url($wsdlFile)['path']), PATHINFO_FILENAME);
        }

        if (!class_exists($originalClassName)) {
            eval(
                $this->getMockObjectGenerator()->generateClassFromWsdl(
                    $wsdlFile,
                    $originalClassName,
                    $methods,
                    $options
                )
            );
        }

        $mockObject = $this->getMockObjectGenerator()->getMock(
            $originalClassName,
            $methods,
            ['', $options],
            $mockClassName,
            $callOriginalConstructor,
            false,
            false
        );

        $this->registerMockObject($mockObject);

        return $mockObject;
    }

    /**
     * Returns a mock object for the specified trait with all abstract methods
     * of the trait mocked. Concrete methods to mock can be specified with the
     * `$mockedMethods` parameter.
     *
     * @param string $traitName
     * @param array  $arguments
     * @param string $mockClassName
     * @param bool   $callOriginalConstructor
     * @param bool   $callOriginalClone
     * @param bool   $callAutoload
     * @param array  $mockedMethods
     * @param bool   $cloneArguments
     *
     * @return PHPUnit_Framework_MockObject_MockObject
     *
     * @throws PHPUnit_Framework_Exception
     */
    protected function getMockForTrait($traitName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false)
    {
        $mockObject = $this->getMockObjectGenerator()->getMockForTrait(
            $traitName,
            $arguments,
            $mockClassName,
            $callOriginalConstructor,
            $callOriginalClone,
            $callAutoload,
            $mockedMethods,
            $cloneArguments
        );

        $this->registerMockObject($mockObject);

        return $mockObject;
    }

    /**
     * Returns an object for the specified trait.
     *
     * @param string $traitName
     * @param array  $arguments
     * @param string $traitClassName
     * @param bool   $callOriginalConstructor
     * @param bool   $callOriginalClone
     * @param bool   $callAutoload
     * @param bool   $cloneArguments
     *
     * @return object
     *
     * @throws PHPUnit_Framework_Exception
     */
    protected function getObjectForTrait($traitName, array $arguments = [], $traitClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false)
    {
        return $this->getMockObjectGenerator()->getObjectForTrait(
            $traitName,
            $arguments,
            $traitClassName,
            $callOriginalConstructor,
            $callOriginalClone,
            $callAutoload,
            $cloneArguments
        );
    }

    /**
     * @param string|null $classOrInterface
     *
     * @return \Prophecy\Prophecy\ObjectProphecy
     *
     * @throws \LogicException
     */
    protected function prophesize($classOrInterface = null)
    {
        return $this->getProphet()->prophesize($classOrInterface);
    }

    /**
     * Adds a value to the assertion counter.
     *
     * @param int $count
     */
    public function addToAssertionCount($count)
    {
        $this->numAssertions += $count;
    }

    /**
     * Returns the number of assertions performed by this test.
     *
     * @return int
     */
    public function getNumAssertions()
    {
        return $this->numAssertions;
    }

    /**
     * Returns a matcher that matches when the method is executed
     * zero or more times.
     *
     * @return PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount
     */
    public static function any()
    {
        return new PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount;
    }

    /**
     * Returns a matcher that matches when the method is never executed.
     *
     * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount
     */
    public static function never()
    {
        return new PHPUnit_Framework_MockObject_Matcher_InvokedCount(0);
    }

    /**
     * Returns a matcher that matches when the method is executed
     * at least N times.
     *
     * @param int $requiredInvocations
     *
     * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastCount
     */
    public static function atLeast($requiredInvocations)
    {
        return new PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastCount(
            $requiredInvocations
        );
    }

    /**
     * Returns a matcher that matches when the method is executed at least once.
     *
     * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce
     */
    public static function atLeastOnce()
    {
        return new PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce;
    }

    /**
     * Returns a matcher that matches when the method is executed exactly once.
     *
     * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount
     */
    public static function once()
    {
        return new PHPUnit_Framework_MockObject_Matcher_InvokedCount(1);
    }

    /**
     * Returns a matcher that matches when the method is executed
     * exactly $count times.
     *
     * @param int $count
     *
     * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount
     */
    public static function exactly($count)
    {
        return new PHPUnit_Framework_MockObject_Matcher_InvokedCount($count);
    }

    /**
     * Returns a matcher that matches when the method is executed
     * at most N times.
     *
     * @param int $allowedInvocations
     *
     * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtMostCount
     */
    public static function atMost($allowedInvocations)
    {
        return new PHPUnit_Framework_MockObject_Matcher_InvokedAtMostCount(
            $allowedInvocations
        );
    }

    /**
     * Returns a matcher that matches when the method is executed
     * at the given index.
     *
     * @param int $index
     *
     * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex
     */
    public static function at($index)
    {
        return new PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex($index);
    }

    /**
     * @param mixed $value
     *
     * @return PHPUnit_Framework_MockObject_Stub_Return
     */
    public static function returnValue($value)
    {
        return new PHPUnit_Framework_MockObject_Stub_Return($value);
    }

    /**
     * @param array $valueMap
     *
     * @return PHPUnit_Framework_MockObject_Stub_ReturnValueMap
     */
    public static function returnValueMap(array $valueMap)
    {
        return new PHPUnit_Framework_MockObject_Stub_ReturnValueMap($valueMap);
    }

    /**
     * @param int $argumentIndex
     *
     * @return PHPUnit_Framework_MockObject_Stub_ReturnArgument
     */
    public static function returnArgument($argumentIndex)
    {
        return new PHPUnit_Framework_MockObject_Stub_ReturnArgument(
            $argumentIndex
        );
    }

    /**
     * @param mixed $callback
     *
     * @return PHPUnit_Framework_MockObject_Stub_ReturnCallback
     */
    public static function returnCallback($callback)
    {
        return new PHPUnit_Framework_MockObject_Stub_ReturnCallback($callback);
    }

    /**
     * Returns the current object.
     *
     * This method is useful when mocking a fluent interface.
     *
     * @return PHPUnit_Framework_MockObject_Stub_ReturnSelf
     */
    public static function returnSelf()
    {
        return new PHPUnit_Framework_MockObject_Stub_ReturnSelf();
    }

    /**
     * @param Throwable|Exception $exception
     *
     * @return PHPUnit_Framework_MockObject_Stub_Exception
     *
     * @todo   Add type declaration when support for PHP 5 is dropped
     */
    public static function throwException($exception)
    {
        return new PHPUnit_Framework_MockObject_Stub_Exception($exception);
    }

    /**
     * @param mixed $value, ...
     *
     * @return PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls
     */
    public static function onConsecutiveCalls()
    {
        $args = func_get_args();

        return new PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls($args);
    }

    /**
     * @return bool
     */
    public function usesDataProvider()
    {
        return !empty($this->data);
    }

    /**
     * @return string
     */
    public function dataDescription()
    {
        return is_string($this->dataName) ? $this->dataName : '';
    }

    /**
     * Gets the data set description of a TestCase.
     *
     * @param bool $includeData
     *
     * @return string
     */
    protected function getDataSetAsString($includeData = true)
    {
        $buffer = '';

        if (!empty($this->data)) {
            if (is_int($this->dataName)) {
                $buffer .= sprintf(' with data set #%d', $this->dataName);
            } else {
                $buffer .= sprintf(' with data set "%s"', $this->dataName);
            }

            $exporter = new Exporter;

            if ($includeData) {
                $buffer .= sprintf(' (%s)', $exporter->shortenedRecursiveExport($this->data));
            }
        }

        return $buffer;
    }

    /**
     * Gets the data set of a TestCase.
     *
     * @return array
     */
    protected function getProvidedData()
    {
        return $this->data;
    }

    /**
     * Creates a default TestResult object.
     *
     * @return PHPUnit_Framework_TestResult
     */
    protected function createResult()
    {
        return new PHPUnit_Framework_TestResult;
    }

    protected function handleDependencies()
    {
        if (!empty($this->dependencies) && !$this->inIsolation) {
            $className  = get_class($this);
            $passed     = $this->result->passed();
            $passedKeys = array_keys($passed);
            $numKeys    = count($passedKeys);

            for ($i = 0; $i < $numKeys; $i++) {
                $pos = strpos($passedKeys[$i], ' with data set');

                if ($pos !== false) {
                    $passedKeys[$i] = substr($passedKeys[$i], 0, $pos);
                }
            }

            $passedKeys = array_flip(array_unique($passedKeys));

            foreach ($this->dependencies as $dependency) {
                $clone = false;

                if (strpos($dependency, 'clone ') === 0) {
                    $clone      = true;
                    $dependency = substr($dependency, strlen('clone '));
                } elseif (strpos($dependency, '!clone ') === 0) {
                    $clone      = false;
                    $dependency = substr($dependency, strlen('!clone '));
                }

                if (strpos($dependency, '::') === false) {
                    $dependency = $className . '::' . $dependency;
                }

                if (!isset($passedKeys[$dependency])) {
                    $this->result->startTest($this);
                    $this->result->addError(
                        $this,
                        new PHPUnit_Framework_SkippedTestError(
                            sprintf(
                                'This test depends on "%s" to pass.',
                                $dependency
                            )
                        ),
                        0
                    );
                    $this->result->endTest($this, 0);

                    return false;
                }

                if (isset($passed[$dependency])) {
                    if ($passed[$dependency]['size'] != PHPUnit_Util_Test::UNKNOWN &&
                        $this->getSize() != PHPUnit_Util_Test::UNKNOWN &&
                        $passed[$dependency]['size'] > $this->getSize()) {
                        $this->result->addError(
                            $this,
                            new PHPUnit_Framework_SkippedTestError(
                                'This test depends on a test that is larger than itself.'
                            ),
                            0
                        );

                        return false;
                    }

                    if ($clone) {
                        $deepCopy   = new DeepCopy;
                        $deepCopy->skipUncloneable(false);

                        $this->dependencyInput[$dependency] = $deepCopy->copy($passed[$dependency]['result']);
                    } else {
                        $this->dependencyInput[$dependency] = $passed[$dependency]['result'];
                    }
                } else {
                    $this->dependencyInput[$dependency] = null;
                }
            }
        }

        return true;
    }

    /**
     * This method is called before the first test of this test class is run.
     */
    public static function setUpBeforeClass()
    {
    }

    /**
     * Sets up the fixture, for example, open a network connection.
     * This method is called before a test is executed.
     */
    protected function setUp()
    {
    }

    /**
     * Performs assertions shared by all tests of a test case.
     *
     * This method is called before the execution of a test starts
     * and after setUp() is called.
     */
    protected function assertPreConditions()
    {
    }

    /**
     * Performs assertions shared by all tests of a test case.
     *
     * This method is called before the execution of a test ends
     * and before tearDown() is called.
     */
    protected function assertPostConditions()
    {
    }

    /**
     * Tears down the fixture, for example, close a network connection.
     * This method is called after a test is executed.
     */
    protected function tearDown()
    {
    }

    /**
     * This method is called after the last test of this test class is run.
     */
    public static function tearDownAfterClass()
    {
    }

    /**
     * This method is called when a test method did not execute successfully.
     *
     * @param Exception|Throwable $e
     *
     * @throws Exception|Throwable
     */
    protected function onNotSuccessfulTest($e)
    {
        $expected = PHP_MAJOR_VERSION >= 7 ? 'Throwable' : 'Exception';

        if ($e instanceof $expected) {
            throw $e;
        }

        throw PHPUnit_Util_InvalidArgumentHelper::factory(
            1,
            'Throwable or Exception'
        );
    }

    /**
     * Performs custom preparations on the process isolation template.
     *
     * @param Text_Template $template
     */
    protected function prepareTemplate(Text_Template $template)
    {
    }

    /**
     * Get the mock object generator, creating it if it doesn't exist.
     *
     * @return PHPUnit_Framework_MockObject_Generator
     */
    protected function getMockObjectGenerator()
    {
        if (null === $this->mockObjectGenerator) {
            $this->mockObjectGenerator = new PHPUnit_Framework_MockObject_Generator;
        }

        return $this->mockObjectGenerator;
    }

    private function startOutputBuffering()
    {
        ob_start();

        $this->outputBufferingActive = true;
        $this->outputBufferingLevel  = ob_get_level();
    }

    private function stopOutputBuffering()
    {
        if (ob_get_level() != $this->outputBufferingLevel) {
            while (ob_get_level() >= $this->outputBufferingLevel) {
                ob_end_clean();
            }

            throw new PHPUnit_Framework_RiskyTestError(
                'Test code or tested code did not (only) close its own output buffers'
            );
        }

        $output = ob_get_contents();

        if ($this->outputCallback === false) {
            $this->output = $output;
        } else {
            $this->output = call_user_func_array(
                $this->outputCallback,
                [$output]
            );
        }

        ob_end_clean();

        $this->outputBufferingActive = false;
        $this->outputBufferingLevel  = ob_get_level();
    }

    private function snapshotGlobalState()
    {
        $backupGlobals = $this->backupGlobals === null || $this->backupGlobals === true;

        if ($this->runTestInSeparateProcess ||
            $this->inIsolation ||
            (!$backupGlobals && !$this->backupStaticAttributes)) {
            return;
        }

        $this->snapshot = $this->createGlobalStateSnapshot($backupGlobals);
    }

    private function restoreGlobalState()
    {
        if (!$this->snapshot instanceof Snapshot) {
            return;
        }

        $backupGlobals = $this->backupGlobals === null || $this->backupGlobals === true;

        if ($this->beStrictAboutChangesToGlobalState) {
            try {
                $this->compareGlobalStateSnapshots(
                    $this->snapshot,
                    $this->createGlobalStateSnapshot($backupGlobals)
                );
            } catch (PHPUnit_Framework_RiskyTestError $rte) {
                // Intentionally left empty
            }
        }

        $restorer = new Restorer;

        if ($backupGlobals) {
            $restorer->restoreGlobalVariables($this->snapshot);
        }

        if ($this->backupStaticAttributes) {
            $restorer->restoreStaticAttributes($this->snapshot);
        }

        $this->snapshot = null;

        if (isset($rte)) {
            throw $rte;
        }
    }

    /**
     * @param bool $backupGlobals
     *
     * @return Snapshot
     */
    private function createGlobalStateSnapshot($backupGlobals)
    {
        $blacklist = new Blacklist;

        foreach ($this->backupGlobalsBlacklist as $globalVariable) {
            $blacklist->addGlobalVariable($globalVariable);
        }

        if (!defined('PHPUNIT_TESTSUITE')) {
            $blacklist->addClassNamePrefix('PHPUnit');
            $blacklist->addClassNamePrefix('File_Iterator');
            $blacklist->addClassNamePrefix('SebastianBergmann\CodeCoverage');
            $blacklist->addClassNamePrefix('PHP_Invoker');
            $blacklist->addClassNamePrefix('PHP_Timer');
            $blacklist->addClassNamePrefix('PHP_Token');
            $blacklist->addClassNamePrefix('Symfony');
            $blacklist->addClassNamePrefix('Text_Template');
            $blacklist->addClassNamePrefix('Doctrine\Instantiator');
            $blacklist->addClassNamePrefix('Prophecy');

            foreach ($this->backupStaticAttributesBlacklist as $class => $attributes) {
                foreach ($attributes as $attribute) {
                    $blacklist->addStaticAttribute($class, $attribute);
                }
            }
        }

        return new Snapshot(
            $blacklist,
            $backupGlobals,
            (bool) $this->backupStaticAttributes,
            false,
            false,
            false,
            false,
            false,
            false,
            false
        );
    }

    /**
     * @param Snapshot $before
     * @param Snapshot $after
     *
     * @throws PHPUnit_Framework_RiskyTestError
     */
    private function compareGlobalStateSnapshots(Snapshot $before, Snapshot $after)
    {
        $backupGlobals = $this->backupGlobals === null || $this->backupGlobals === true;

        if ($backupGlobals) {
            $this->compareGlobalStateSnapshotPart(
                $before->globalVariables(),
                $after->globalVariables(),
                "--- Global variables before the test\n+++ Global variables after the test\n"
            );

            $this->compareGlobalStateSnapshotPart(
                $before->superGlobalVariables(),
                $after->superGlobalVariables(),
                "--- Super-global variables before the test\n+++ Super-global variables after the test\n"
            );
        }

        if ($this->backupStaticAttributes) {
            $this->compareGlobalStateSnapshotPart(
                $before->staticAttributes(),
                $after->staticAttributes(),
                "--- Static attributes before the test\n+++ Static attributes after the test\n"
            );
        }
    }

    /**
     * @param array  $before
     * @param array  $after
     * @param string $header
     *
     * @throws PHPUnit_Framework_RiskyTestError
     */
    private function compareGlobalStateSnapshotPart(array $before, array $after, $header)
    {
        if ($before != $after) {
            $differ   = new Differ($header);
            $exporter = new Exporter;

            $diff = $differ->diff(
                $exporter->export($before),
                $exporter->export($after)
            );

            throw new PHPUnit_Framework_RiskyTestError(
                $diff
            );
        }
    }

    /**
     * @return Prophecy\Prophet
     */
    private function getProphet()
    {
        if ($this->prophet === null) {
            $this->prophet = new Prophet;
        }

        return $this->prophet;
    }

    /**
     * @param PHPUnit_Framework_MockObject_MockObject $mock
     *
     * @return bool
     */
    private function shouldInvocationMockerBeReset(PHPUnit_Framework_MockObject_MockObject $mock)
    {
        $enumerator = new Enumerator;

        foreach ($enumerator->enumerate($this->dependencyInput) as $object) {
            if ($mock === $object) {
                return false;
            }
        }

        if (!is_array($this->testResult) && !is_object($this->testResult)) {
            return true;
        }

        foreach ($enumerator->enumerate($this->testResult) as $object) {
            if ($mock === $object) {
                return false;
            }
        }

        return true;
    }

    /**
     * @param array $testArguments
     * @param array $originalTestArguments
     */
    private function registerMockObjectsFromTestArguments(array $testArguments, array &$visited = [])
    {
        if ($this->registerMockObjectsFromTestArgumentsRecursively) {
            $enumerator = new Enumerator;

            foreach ($enumerator->enumerate($testArguments) as $object) {
                if ($object instanceof PHPUnit_Framework_MockObject_MockObject) {
                    $this->registerMockObject($object);
                }
            }
        } else {
            foreach ($testArguments as $testArgument) {
                if ($testArgument instanceof PHPUnit_Framework_MockObject_MockObject) {
                    if ($this->isCloneable($testArgument)) {
                        $testArgument = clone $testArgument;
                    }

                    $this->registerMockObject($testArgument);
                } elseif (is_array($testArgument) && !in_array($testArgument, $visited, true)) {
                    $visited[] = $testArgument;

                    $this->registerMockObjectsFromTestArguments(
                        $testArgument,
                        $visited
                    );
                }
            }
        }
    }

    private function setDoesNotPerformAssertionsFromAnnotation()
    {
        $annotations = $this->getAnnotations();

        if (isset($annotations['method']['doesNotPerformAssertions'])) {
            $this->doesNotPerformAssertions = true;
        }
    }

    /**
     * @param PHPUnit_Framework_MockObject_MockObject $testArgument
     *
     * @return bool
     */
    private function isCloneable(PHPUnit_Framework_MockObject_MockObject $testArgument)
    {
        $reflector = new ReflectionObject($testArgument);

        if (!$reflector->isCloneable()) {
            return false;
        }

        if ($reflector->hasMethod('__clone') &&
            $reflector->getMethod('__clone')->isPublic()) {
            return true;
        }

        return false;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A TestFailure collects a failed test together with the caught exception.
 */
class PHPUnit_Framework_TestFailure
{
    /**
     * @var string
     */
    private $testName;

    /**
     * @var PHPUnit_Framework_Test|null
     */
    protected $failedTest;

    /**
     * @var Exception
     */
    protected $thrownException;

    /**
     * Constructs a TestFailure with the given test and exception.
     *
     * @param PHPUnit_Framework_Test $failedTest
     * @param Throwable              $t
     */
    public function __construct(PHPUnit_Framework_Test $failedTest, $t)
    {
        if ($failedTest instanceof PHPUnit_Framework_SelfDescribing) {
            $this->testName = $failedTest->toString();
        } else {
            $this->testName = get_class($failedTest);
        }

        if (!$failedTest instanceof PHPUnit_Framework_TestCase || !$failedTest->isInIsolation()) {
            $this->failedTest = $failedTest;
        }

        $this->thrownException = $t;
    }

    /**
     * Returns a short description of the failure.
     *
     * @return string
     */
    public function toString()
    {
        return sprintf(
            '%s: %s',
            $this->testName,
            $this->thrownException->getMessage()
        );
    }

    /**
     * Returns a description for the thrown exception.
     *
     * @return string
     */
    public function getExceptionAsString()
    {
        return self::exceptionToString($this->thrownException);
    }

    /**
     * Returns a description for an exception.
     *
     * @param Exception $e
     *
     * @return string
     */
    public static function exceptionToString(Exception $e)
    {
        if ($e instanceof PHPUnit_Framework_SelfDescribing) {
            $buffer = $e->toString();

            if ($e instanceof PHPUnit_Framework_ExpectationFailedException && $e->getComparisonFailure()) {
                $buffer = $buffer . $e->getComparisonFailure()->getDiff();
            }

            if (!empty($buffer)) {
                $buffer = trim($buffer) . "\n";
            }
        } elseif ($e instanceof PHPUnit_Framework_Error) {
            $buffer = $e->getMessage() . "\n";
        } elseif ($e instanceof PHPUnit_Framework_ExceptionWrapper) {
            $buffer = $e->getClassName() . ': ' . $e->getMessage() . "\n";
        } else {
            $buffer = get_class($e) . ': ' . $e->getMessage() . "\n";
        }

        return $buffer;
    }

    /**
     * Returns the name of the failing test (including data set, if any).
     *
     * @return string
     */
    public function getTestName()
    {
        return $this->testName;
    }

    /**
     * Returns the failing test.
     *
     * Note: The test object is not set when the test is executed in process
     * isolation.
     *
     * @see PHPUnit_Framework_Exception
     *
     * @return PHPUnit_Framework_Test|null
     */
    public function failedTest()
    {
        return $this->failedTest;
    }

    /**
     * Gets the thrown exception.
     *
     * @return Exception
     */
    public function thrownException()
    {
        return $this->thrownException;
    }

    /**
     * Returns the exception's message.
     *
     * @return string
     */
    public function exceptionMessage()
    {
        return $this->thrownException()->getMessage();
    }

    /**
     * Returns true if the thrown exception
     * is of type AssertionFailedError.
     *
     * @return bool
     */
    public function isFailure()
    {
        return ($this->thrownException() instanceof PHPUnit_Framework_AssertionFailedError);
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A Listener for test progress.
 */
interface PHPUnit_Framework_TestListener
{
    /**
     * An error occurred.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time);

    /**
     * A warning occurred.
     *
     * @param PHPUnit_Framework_Test    $test
     * @param PHPUnit_Framework_Warning $e
     * @param float                     $time
     *
     * @todo  Uncomment in time for PHPUnit 6.0.0
     *
     * @see   https://github.com/sebastianbergmann/phpunit/pull/1840#issuecomment-162535997
     */
//  public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time);

    /**
     * A failure occurred.
     *
     * @param PHPUnit_Framework_Test                 $test
     * @param PHPUnit_Framework_AssertionFailedError $e
     * @param float                                  $time
     */
    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time);

    /**
     * Incomplete test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time);

    /**
     * Risky test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time);

    /**
     * Skipped test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time);

    /**
     * A test suite started.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite);

    /**
     * A test suite ended.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite);

    /**
     * A test started.
     *
     * @param PHPUnit_Framework_Test $test
     */
    public function startTest(PHPUnit_Framework_Test $test);

    /**
     * A test ended.
     *
     * @param PHPUnit_Framework_Test $test
     * @param float                  $time
     */
    public function endTest(PHPUnit_Framework_Test $test, $time);
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Exception as CodeCoverageException;
use SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException;
use SebastianBergmann\CodeCoverage\MissingCoversAnnotationException;
use SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException;
use SebastianBergmann\ResourceOperations\ResourceOperations;

/**
 * A TestResult collects the results of executing a test case.
 */
class PHPUnit_Framework_TestResult implements Countable
{
    /**
     * @var array
     */
    protected $passed = [];

    /**
     * @var array
     */
    protected $errors = [];

    /**
     * @var array
     */
    protected $failures = [];

    /**
     * @var array
     */
    protected $warnings = [];

    /**
     * @var array
     */
    protected $notImplemented = [];

    /**
     * @var array
     */
    protected $risky = [];

    /**
     * @var array
     */
    protected $skipped = [];

    /**
     * @var array
     */
    protected $listeners = [];

    /**
     * @var int
     */
    protected $runTests = 0;

    /**
     * @var float
     */
    protected $time = 0;

    /**
     * @var PHPUnit_Framework_TestSuite
     */
    protected $topTestSuite = null;

    /**
     * Code Coverage information.
     *
     * @var CodeCoverage
     */
    protected $codeCoverage;

    /**
     * @var bool
     */
    protected $convertErrorsToExceptions = true;

    /**
     * @var bool
     */
    protected $stop = false;

    /**
     * @var bool
     */
    protected $stopOnError = false;

    /**
     * @var bool
     */
    protected $stopOnFailure = false;

    /**
     * @var bool
     */
    protected $stopOnWarning = false;

    /**
     * @var bool
     */
    protected $beStrictAboutTestsThatDoNotTestAnything = false;

    /**
     * @var bool
     */
    protected $beStrictAboutOutputDuringTests = false;

    /**
     * @var bool
     */
    protected $beStrictAboutTodoAnnotatedTests = false;

    /**
     * @var bool
     */
    protected $beStrictAboutResourceUsageDuringSmallTests = false;

    /**
     * @var bool
     */
    protected $enforceTimeLimit = false;

    /**
     * @var int
     */
    protected $timeoutForSmallTests = 1;

    /**
     * @var int
     */
    protected $timeoutForMediumTests = 10;

    /**
     * @var int
     */
    protected $timeoutForLargeTests = 60;

    /**
     * @var bool
     */
    protected $stopOnRisky = false;

    /**
     * @var bool
     */
    protected $stopOnIncomplete = false;

    /**
     * @var bool
     */
    protected $stopOnSkipped = false;

    /**
     * @var bool
     */
    protected $lastTestFailed = false;

    /**
     * @var bool
     */
    private $registerMockObjectsFromTestArgumentsRecursively = false;

    /**
     * Registers a TestListener.
     *
     * @param  PHPUnit_Framework_TestListener
     */
    public function addListener(PHPUnit_Framework_TestListener $listener)
    {
        $this->listeners[] = $listener;
    }

    /**
     * Unregisters a TestListener.
     *
     * @param PHPUnit_Framework_TestListener $listener
     */
    public function removeListener(PHPUnit_Framework_TestListener $listener)
    {
        foreach ($this->listeners as $key => $_listener) {
            if ($listener === $_listener) {
                unset($this->listeners[$key]);
            }
        }
    }

    /**
     * Flushes all flushable TestListeners.
     */
    public function flushListeners()
    {
        foreach ($this->listeners as $listener) {
            if ($listener instanceof PHPUnit_Util_Printer) {
                $listener->flush();
            }
        }
    }

    /**
     * Adds an error to the list of errors.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Throwable              $t
     * @param float                  $time
     */
    public function addError(PHPUnit_Framework_Test $test, $t, $time)
    {
        if ($t instanceof PHPUnit_Framework_RiskyTest) {
            $this->risky[] = new PHPUnit_Framework_TestFailure($test, $t);
            $notifyMethod  = 'addRiskyTest';

            if ($test instanceof PHPUnit_Framework_TestCase) {
                $test->markAsRisky();
            }

            if ($this->stopOnRisky) {
                $this->stop();
            }
        } elseif ($t instanceof PHPUnit_Framework_IncompleteTest) {
            $this->notImplemented[] = new PHPUnit_Framework_TestFailure($test, $t);
            $notifyMethod           = 'addIncompleteTest';

            if ($this->stopOnIncomplete) {
                $this->stop();
            }
        } elseif ($t instanceof PHPUnit_Framework_SkippedTest) {
            $this->skipped[] = new PHPUnit_Framework_TestFailure($test, $t);
            $notifyMethod    = 'addSkippedTest';

            if ($this->stopOnSkipped) {
                $this->stop();
            }
        } else {
            $this->errors[] = new PHPUnit_Framework_TestFailure($test, $t);
            $notifyMethod   = 'addError';

            if ($this->stopOnError || $this->stopOnFailure) {
                $this->stop();
            }
        }

        // @see https://github.com/sebastianbergmann/phpunit/issues/1953
        if ($t instanceof Error) {
            $t = new PHPUnit_Framework_ExceptionWrapper($t);
        }

        foreach ($this->listeners as $listener) {
            $listener->$notifyMethod($test, $t, $time);
        }

        $this->lastTestFailed = true;
        $this->time          += $time;
    }

    /**
     * Adds a warning to the list of warnings.
     * The passed in exception caused the warning.
     *
     * @param PHPUnit_Framework_Test    $test
     * @param PHPUnit_Framework_Warning $e
     * @param float                     $time
     */
    public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
    {
        if ($this->stopOnWarning) {
            $this->stop();
        }

        $this->warnings[] = new PHPUnit_Framework_TestFailure($test, $e);

        foreach ($this->listeners as $listener) {
            // @todo Remove check for PHPUnit 6.0.0
            // @see  https://github.com/sebastianbergmann/phpunit/pull/1840#issuecomment-162535997
            if (method_exists($listener, 'addWarning')) {
                $listener->addWarning($test, $e, $time);
            }
        }

        $this->time += $time;
    }

    /**
     * Adds a failure to the list of failures.
     * The passed in exception caused the failure.
     *
     * @param PHPUnit_Framework_Test                 $test
     * @param PHPUnit_Framework_AssertionFailedError $e
     * @param float                                  $time
     */
    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
    {
        if ($e instanceof PHPUnit_Framework_RiskyTest ||
            $e instanceof PHPUnit_Framework_OutputError) {
            $this->risky[] = new PHPUnit_Framework_TestFailure($test, $e);
            $notifyMethod  = 'addRiskyTest';

            if ($test instanceof PHPUnit_Framework_TestCase) {
                $test->markAsRisky();
            }

            if ($this->stopOnRisky) {
                $this->stop();
            }
        } elseif ($e instanceof PHPUnit_Framework_IncompleteTest) {
            $this->notImplemented[] = new PHPUnit_Framework_TestFailure($test, $e);
            $notifyMethod           = 'addIncompleteTest';

            if ($this->stopOnIncomplete) {
                $this->stop();
            }
        } elseif ($e instanceof PHPUnit_Framework_SkippedTest) {
            $this->skipped[] = new PHPUnit_Framework_TestFailure($test, $e);
            $notifyMethod    = 'addSkippedTest';

            if ($this->stopOnSkipped) {
                $this->stop();
            }
        } else {
            $this->failures[] = new PHPUnit_Framework_TestFailure($test, $e);
            $notifyMethod     = 'addFailure';

            if ($this->stopOnFailure) {
                $this->stop();
            }
        }

        foreach ($this->listeners as $listener) {
            $listener->$notifyMethod($test, $e, $time);
        }

        $this->lastTestFailed = true;
        $this->time          += $time;
    }

    /**
     * Informs the result that a testsuite will be started.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        if ($this->topTestSuite === null) {
            $this->topTestSuite = $suite;
        }

        foreach ($this->listeners as $listener) {
            $listener->startTestSuite($suite);
        }
    }

    /**
     * Informs the result that a testsuite was completed.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        foreach ($this->listeners as $listener) {
            $listener->endTestSuite($suite);
        }
    }

    /**
     * Informs the result that a test will be started.
     *
     * @param PHPUnit_Framework_Test $test
     */
    public function startTest(PHPUnit_Framework_Test $test)
    {
        $this->lastTestFailed = false;
        $this->runTests      += count($test);

        foreach ($this->listeners as $listener) {
            $listener->startTest($test);
        }
    }

    /**
     * Informs the result that a test was completed.
     *
     * @param PHPUnit_Framework_Test $test
     * @param float                  $time
     */
    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
        foreach ($this->listeners as $listener) {
            $listener->endTest($test, $time);
        }

        if (!$this->lastTestFailed && $test instanceof PHPUnit_Framework_TestCase) {
            $class  = get_class($test);
            $key    = $class . '::' . $test->getName();

            $this->passed[$key] = [
                'result' => $test->getResult(),
                'size'   => PHPUnit_Util_Test::getSize(
                    $class,
                    $test->getName(false)
                )
            ];

            $this->time += $time;
        }
    }

    /**
     * Returns true if no risky test occurred.
     *
     * @return bool
     */
    public function allHarmless()
    {
        return $this->riskyCount() == 0;
    }

    /**
     * Gets the number of risky tests.
     *
     * @return int
     */
    public function riskyCount()
    {
        return count($this->risky);
    }

    /**
     * Returns true if no incomplete test occurred.
     *
     * @return bool
     */
    public function allCompletelyImplemented()
    {
        return $this->notImplementedCount() == 0;
    }

    /**
     * Gets the number of incomplete tests.
     *
     * @return int
     */
    public function notImplementedCount()
    {
        return count($this->notImplemented);
    }

    /**
     * Returns an Enumeration for the risky tests.
     *
     * @return array
     */
    public function risky()
    {
        return $this->risky;
    }

    /**
     * Returns an Enumeration for the incomplete tests.
     *
     * @return array
     */
    public function notImplemented()
    {
        return $this->notImplemented;
    }

    /**
     * Returns true if no test has been skipped.
     *
     * @return bool
     */
    public function noneSkipped()
    {
        return $this->skippedCount() == 0;
    }

    /**
     * Gets the number of skipped tests.
     *
     * @return int
     */
    public function skippedCount()
    {
        return count($this->skipped);
    }

    /**
     * Returns an Enumeration for the skipped tests.
     *
     * @return array
     */
    public function skipped()
    {
        return $this->skipped;
    }

    /**
     * Gets the number of detected errors.
     *
     * @return int
     */
    public function errorCount()
    {
        return count($this->errors);
    }

    /**
     * Returns an Enumeration for the errors.
     *
     * @return array
     */
    public function errors()
    {
        return $this->errors;
    }

    /**
     * Gets the number of detected failures.
     *
     * @return int
     */
    public function failureCount()
    {
        return count($this->failures);
    }

    /**
     * Returns an Enumeration for the failures.
     *
     * @return array
     */
    public function failures()
    {
        return $this->failures;
    }

    /**
     * Gets the number of detected warnings.
     *
     * @return int
     */
    public function warningCount()
    {
        return count($this->warnings);
    }

    /**
     * Returns an Enumeration for the warnings.
     *
     * @return array
     */
    public function warnings()
    {
        return $this->warnings;
    }

    /**
     * Returns the names of the tests that have passed.
     *
     * @return array
     */
    public function passed()
    {
        return $this->passed;
    }

    /**
     * Returns the (top) test suite.
     *
     * @return PHPUnit_Framework_TestSuite
     */
    public function topTestSuite()
    {
        return $this->topTestSuite;
    }

    /**
     * Returns whether code coverage information should be collected.
     *
     * @return bool If code coverage should be collected
     */
    public function getCollectCodeCoverageInformation()
    {
        return $this->codeCoverage !== null;
    }

    /**
     * Runs a TestCase.
     *
     * @param PHPUnit_Framework_Test $test
     */
    public function run(PHPUnit_Framework_Test $test)
    {
        PHPUnit_Framework_Assert::resetCount();

        $coversNothing = false;

        if ($test instanceof PHPUnit_Framework_TestCase) {
            $test->setRegisterMockObjectsFromTestArgumentsRecursively(
                $this->registerMockObjectsFromTestArgumentsRecursively
            );

            $annotations = $test->getAnnotations();

            if (isset($annotations['class']['coversNothing']) || isset($annotations['method']['coversNothing'])) {
                $coversNothing = true;
            }
        }

        $error      = false;
        $failure    = false;
        $warning    = false;
        $incomplete = false;
        $risky      = false;
        $skipped    = false;

        $this->startTest($test);

        $errorHandlerSet = false;

        if ($this->convertErrorsToExceptions) {
            $oldErrorHandler = set_error_handler(
                ['PHPUnit_Util_ErrorHandler', 'handleError'],
                E_ALL | E_STRICT
            );

            if ($oldErrorHandler === null) {
                $errorHandlerSet = true;
            } else {
                restore_error_handler();
            }
        }

        $collectCodeCoverage = $this->codeCoverage !== null &&
                               !$test instanceof PHPUnit_Framework_WarningTestCase &&
                               !$coversNothing;

        if ($collectCodeCoverage) {
            $this->codeCoverage->start($test);
        }

        $monitorFunctions = $this->beStrictAboutResourceUsageDuringSmallTests &&
                            !$test instanceof PHPUnit_Framework_WarningTestCase &&
                            $test->getSize() == PHPUnit_Util_Test::SMALL &&
                            function_exists('xdebug_start_function_monitor');

        if ($monitorFunctions) {
            xdebug_start_function_monitor(ResourceOperations::getFunctions());
        }

        PHP_Timer::start();

        try {
            if (!$test instanceof PHPUnit_Framework_WarningTestCase &&
                $test->getSize() != PHPUnit_Util_Test::UNKNOWN &&
                $this->enforceTimeLimit &&
                extension_loaded('pcntl') && class_exists('PHP_Invoker')) {
                switch ($test->getSize()) {
                    case PHPUnit_Util_Test::SMALL:
                        $_timeout = $this->timeoutForSmallTests;
                        break;

                    case PHPUnit_Util_Test::MEDIUM:
                        $_timeout = $this->timeoutForMediumTests;
                        break;

                    case PHPUnit_Util_Test::LARGE:
                        $_timeout = $this->timeoutForLargeTests;
                        break;
                }

                $invoker = new PHP_Invoker;
                $invoker->invoke([$test, 'runBare'], [], $_timeout);
            } else {
                $test->runBare();
            }
        } catch (PHP_Invoker_TimeoutException $e) {
            $this->addFailure(
                $test,
                new PHPUnit_Framework_RiskyTestError(
                    $e->getMessage()
                ),
                $_timeout
            );

            $risky = true;
        } catch (PHPUnit_Framework_MockObject_Exception $e) {
            $e = new PHPUnit_Framework_Warning(
                $e->getMessage()
            );

            $warning = true;
        } catch (PHPUnit_Framework_AssertionFailedError $e) {
            $failure = true;

            if ($e instanceof PHPUnit_Framework_RiskyTestError) {
                $risky = true;
            } elseif ($e instanceof PHPUnit_Framework_IncompleteTestError) {
                $incomplete = true;
            } elseif ($e instanceof PHPUnit_Framework_SkippedTestError) {
                $skipped = true;
            }
        } catch (PHPUnit_Framework_Warning $e) {
            $warning = true;
        } catch (PHPUnit_Framework_Exception $e) {
            $error = true;
        } catch (Throwable $e) {
            // @see https://github.com/sebastianbergmann/phpunit/issues/2394
            if (PHP_MAJOR_VERSION === 7 && $e instanceof \AssertionError) {
                $test->addToAssertionCount(1);

                $failure = true;
                $frame   = $e->getTrace()[0];

                $e = new PHPUnit_Framework_AssertionFailedError(
                    sprintf(
                        '%s in %s:%s',
                        $e->getMessage(),
                        $frame['file'],
                        $frame['line']
                    )
                );
            } else {
                $e     = new PHPUnit_Framework_ExceptionWrapper($e);
                $error = true;
            }
        } catch (Exception $e) {
            $e     = new PHPUnit_Framework_ExceptionWrapper($e);
            $error = true;
        }

        $time = PHP_Timer::stop();
        $test->addToAssertionCount(PHPUnit_Framework_Assert::getCount());

        if ($monitorFunctions) {
            $blacklist = new PHPUnit_Util_Blacklist;
            $functions = xdebug_get_monitored_functions();
            xdebug_stop_function_monitor();

            foreach ($functions as $function) {
                if (!$blacklist->isBlacklisted($function['filename'])) {
                    $this->addFailure(
                        $test,
                        new PHPUnit_Framework_RiskyTestError(
                            sprintf(
                                '%s() used in %s:%s',
                                $function['function'],
                                $function['filename'],
                                $function['lineno']
                            )
                        ),
                        $time
                    );
                }
            }
        }

        if ($this->beStrictAboutTestsThatDoNotTestAnything &&
            $test->getNumAssertions() == 0) {
            $risky = true;
        }

        if ($collectCodeCoverage) {
            $append           = !$risky && !$incomplete && !$skipped;
            $linesToBeCovered = [];
            $linesToBeUsed    = [];

            if ($append && $test instanceof PHPUnit_Framework_TestCase) {
                try {
                    $linesToBeCovered = PHPUnit_Util_Test::getLinesToBeCovered(
                        get_class($test),
                        $test->getName(false)
                    );

                    $linesToBeUsed = PHPUnit_Util_Test::getLinesToBeUsed(
                        get_class($test),
                        $test->getName(false)
                    );
                } catch (PHPUnit_Framework_InvalidCoversTargetException $cce) {
                    $this->addWarning(
                        $test,
                        new PHPUnit_Framework_Warning(
                            $cce->getMessage()
                        ),
                        $time
                    );
                }
            }

            try {
                $this->codeCoverage->stop(
                    $append,
                    $linesToBeCovered,
                    $linesToBeUsed
                );
            } catch (UnintentionallyCoveredCodeException $cce) {
                $this->addFailure(
                    $test,
                    new PHPUnit_Framework_UnintentionallyCoveredCodeError(
                        'This test executed code that is not listed as code to be covered or used:' .
                        PHP_EOL . $cce->getMessage()
                    ),
                    $time
                );
            } catch (CoveredCodeNotExecutedException $cce) {
                $this->addFailure(
                    $test,
                    new PHPUnit_Framework_CoveredCodeNotExecutedException(
                        'This test did not execute all the code that is listed as code to be covered:' .
                        PHP_EOL . $cce->getMessage()
                    ),
                    $time
                );
            } catch (MissingCoversAnnotationException $cce) {
                if ($linesToBeCovered !== false) {
                    $this->addFailure(
                        $test,
                        new PHPUnit_Framework_MissingCoversAnnotationException(
                            'This test does not have a @covers annotation but is expected to have one'
                        ),
                        $time
                    );
                }
            } catch (CodeCoverageException $cce) {
                $error = true;

                if (!isset($e)) {
                    $e = $cce;
                }
            }
        }

        if ($errorHandlerSet === true) {
            restore_error_handler();
        }

        if ($error === true) {
            $this->addError($test, $e, $time);
        } elseif ($failure === true) {
            $this->addFailure($test, $e, $time);
        } elseif ($warning === true) {
            $this->addWarning($test, $e, $time);
        } elseif ($this->beStrictAboutTestsThatDoNotTestAnything &&
                  !$test->doesNotPerformAssertions() &&
                  $test->getNumAssertions() == 0) {
            $this->addFailure(
                $test,
                new PHPUnit_Framework_RiskyTestError(
                    'This test did not perform any assertions'
                ),
                $time
            );
        } elseif ($this->beStrictAboutOutputDuringTests && $test->hasOutput()) {
            $this->addFailure(
                $test,
                new PHPUnit_Framework_OutputError(
                    sprintf(
                        'This test printed output: %s',
                        $test->getActualOutput()
                    )
                ),
                $time
            );
        } elseif ($this->beStrictAboutTodoAnnotatedTests && $test instanceof PHPUnit_Framework_TestCase) {
            $annotations = $test->getAnnotations();

            if (isset($annotations['method']['todo'])) {
                $this->addFailure(
                    $test,
                    new PHPUnit_Framework_RiskyTestError(
                        'Test method is annotated with @todo'
                    ),
                    $time
                );
            }
        }

        $this->endTest($test, $time);
    }

    /**
     * Gets the number of run tests.
     *
     * @return int
     */
    public function count()
    {
        return $this->runTests;
    }

    /**
     * Checks whether the test run should stop.
     *
     * @return bool
     */
    public function shouldStop()
    {
        return $this->stop;
    }

    /**
     * Marks that the test run should stop.
     */
    public function stop()
    {
        $this->stop = true;
    }

    /**
     * Returns the code coverage object.
     *
     * @return CodeCoverage
     */
    public function getCodeCoverage()
    {
        return $this->codeCoverage;
    }

    /**
     * Sets the code coverage object.
     *
     * @param CodeCoverage $codeCoverage
     */
    public function setCodeCoverage(CodeCoverage $codeCoverage)
    {
        $this->codeCoverage = $codeCoverage;
    }

    /**
     * Enables or disables the error-to-exception conversion.
     *
     * @param bool $flag
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function convertErrorsToExceptions($flag)
    {
        if (!is_bool($flag)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }

        $this->convertErrorsToExceptions = $flag;
    }

    /**
     * Returns the error-to-exception conversion setting.
     *
     * @return bool
     */
    public function getConvertErrorsToExceptions()
    {
        return $this->convertErrorsToExceptions;
    }

    /**
     * Enables or disables the stopping when an error occurs.
     *
     * @param bool $flag
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function stopOnError($flag)
    {
        if (!is_bool($flag)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }

        $this->stopOnError = $flag;
    }

    /**
     * Enables or disables the stopping when a failure occurs.
     *
     * @param bool $flag
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function stopOnFailure($flag)
    {
        if (!is_bool($flag)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }

        $this->stopOnFailure = $flag;
    }

    /**
     * Enables or disables the stopping when a warning occurs.
     *
     * @param bool $flag
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function stopOnWarning($flag)
    {
        if (!is_bool($flag)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }

        $this->stopOnWarning = $flag;
    }

    /**
     * @param bool $flag
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function beStrictAboutTestsThatDoNotTestAnything($flag)
    {
        if (!is_bool($flag)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }

        $this->beStrictAboutTestsThatDoNotTestAnything = $flag;
    }

    /**
     * @return bool
     */
    public function isStrictAboutTestsThatDoNotTestAnything()
    {
        return $this->beStrictAboutTestsThatDoNotTestAnything;
    }

    /**
     * @param bool $flag
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function beStrictAboutOutputDuringTests($flag)
    {
        if (!is_bool($flag)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }

        $this->beStrictAboutOutputDuringTests = $flag;
    }

    /**
     * @return bool
     */
    public function isStrictAboutOutputDuringTests()
    {
        return $this->beStrictAboutOutputDuringTests;
    }

    /**
     * @param bool $flag
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function beStrictAboutResourceUsageDuringSmallTests($flag)
    {
        if (!is_bool($flag)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }

        $this->beStrictAboutResourceUsageDuringSmallTests = $flag;
    }

    /**
     * @return bool
     */
    public function isStrictAboutResourceUsageDuringSmallTests()
    {
        return $this->beStrictAboutResourceUsageDuringSmallTests;
    }

    /**
     * @param bool $flag
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function enforceTimeLimit($flag)
    {
        if (!is_bool($flag)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }

        $this->enforceTimeLimit = $flag;
    }

    /**
     * @return bool
     */
    public function enforcesTimeLimit()
    {
        return $this->enforceTimeLimit;
    }

    /**
     * @param bool $flag
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function beStrictAboutTodoAnnotatedTests($flag)
    {
        if (!is_bool($flag)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }

        $this->beStrictAboutTodoAnnotatedTests = $flag;
    }

    /**
     * @return bool
     */
    public function isStrictAboutTodoAnnotatedTests()
    {
        return $this->beStrictAboutTodoAnnotatedTests;
    }

    /**
     * Enables or disables the stopping for risky tests.
     *
     * @param bool $flag
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function stopOnRisky($flag)
    {
        if (!is_bool($flag)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }

        $this->stopOnRisky = $flag;
    }

    /**
     * Enables or disables the stopping for incomplete tests.
     *
     * @param bool $flag
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function stopOnIncomplete($flag)
    {
        if (!is_bool($flag)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }

        $this->stopOnIncomplete = $flag;
    }

    /**
     * Enables or disables the stopping for skipped tests.
     *
     * @param bool $flag
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function stopOnSkipped($flag)
    {
        if (!is_bool($flag)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }

        $this->stopOnSkipped = $flag;
    }

    /**
     * Returns the time spent running the tests.
     *
     * @return float
     */
    public function time()
    {
        return $this->time;
    }

    /**
     * Returns whether the entire test was successful or not.
     *
     * @param bool $includeWarnings
     *
     * @return bool
     */
    public function wasSuccessful($includeWarnings = true)
    {
        if ($includeWarnings) {
            return empty($this->errors) && empty($this->failures) && empty($this->warnings);
        } else {
            return empty($this->errors) && empty($this->failures);
        }
    }

    /**
     * Sets the timeout for small tests.
     *
     * @param int $timeout
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function setTimeoutForSmallTests($timeout)
    {
        if (!is_int($timeout)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer');
        }

        $this->timeoutForSmallTests = $timeout;
    }

    /**
     * Sets the timeout for medium tests.
     *
     * @param int $timeout
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function setTimeoutForMediumTests($timeout)
    {
        if (!is_int($timeout)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer');
        }

        $this->timeoutForMediumTests = $timeout;
    }

    /**
     * Sets the timeout for large tests.
     *
     * @param int $timeout
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function setTimeoutForLargeTests($timeout)
    {
        if (!is_int($timeout)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer');
        }

        $this->timeoutForLargeTests = $timeout;
    }

    /**
     * Returns the set timeout for large tests.
     *
     * @return int
     */
    public function getTimeoutForLargeTests()
    {
        return $this->timeoutForLargeTests;
    }

    /**
     * @param bool $flag
     */
    public function setRegisterMockObjectsFromTestArgumentsRecursively($flag)
    {
        if (!is_bool($flag)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }

        $this->registerMockObjectsFromTestArgumentsRecursively = $flag;
    }

    /**
     * Returns the class hierarchy for a given class.
     *
     * @param string $className
     * @param bool   $asReflectionObjects
     *
     * @return array
     */
    protected function getHierarchy($className, $asReflectionObjects = false)
    {
        if ($asReflectionObjects) {
            $classes = [new ReflectionClass($className)];
        } else {
            $classes = [$className];
        }

        $done = false;

        while (!$done) {
            if ($asReflectionObjects) {
                $class = new ReflectionClass(
                    $classes[count($classes) - 1]->getName()
                );
            } else {
                $class = new ReflectionClass($classes[count($classes) - 1]);
            }

            $parent = $class->getParentClass();

            if ($parent !== false) {
                if ($asReflectionObjects) {
                    $classes[] = $parent;
                } else {
                    $classes[] = $parent->getName();
                }
            } else {
                $done = true;
            }
        }

        return $classes;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Framework_TestSuite_DataProvider extends PHPUnit_Framework_TestSuite
{
    /**
     * Sets the dependencies of a TestCase.
     *
     * @param array $dependencies
     */
    public function setDependencies(array $dependencies)
    {
        foreach ($this->tests as $test) {
            $test->setDependencies($dependencies);
        }
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A TestSuite is a composite of Tests. It runs a collection of test cases.
 */
class PHPUnit_Framework_TestSuite implements PHPUnit_Framework_Test, PHPUnit_Framework_SelfDescribing, IteratorAggregate
{
    /**
     * Last count of tests in this suite.
     *
     * @var int|null
     */
    private $cachedNumTests;

    /**
     * Enable or disable the backup and restoration of the $GLOBALS array.
     *
     * @var bool
     */
    protected $backupGlobals = null;

    /**
     * Enable or disable the backup and restoration of static attributes.
     *
     * @var bool
     */
    protected $backupStaticAttributes = null;

    /**
     * @var bool
     */
    private $beStrictAboutChangesToGlobalState = null;

    /**
     * @var bool
     */
    protected $runTestInSeparateProcess = false;

    /**
     * The name of the test suite.
     *
     * @var string
     */
    protected $name = '';

    /**
     * The test groups of the test suite.
     *
     * @var array
     */
    protected $groups = [];

    /**
     * The tests in the test suite.
     *
     * @var array
     */
    protected $tests = [];

    /**
     * The number of tests in the test suite.
     *
     * @var int
     */
    protected $numTests = -1;

    /**
     * @var bool
     */
    protected $testCase = false;

    /**
     * @var array
     */
    protected $foundClasses = [];

    /**
     * @var PHPUnit_Runner_Filter_Factory
     */
    private $iteratorFilter = null;

    /**
     * Constructs a new TestSuite:
     *
     *   - PHPUnit_Framework_TestSuite() constructs an empty TestSuite.
     *
     *   - PHPUnit_Framework_TestSuite(ReflectionClass) constructs a
     *     TestSuite from the given class.
     *
     *   - PHPUnit_Framework_TestSuite(ReflectionClass, String)
     *     constructs a TestSuite from the given class with the given
     *     name.
     *
     *   - PHPUnit_Framework_TestSuite(String) either constructs a
     *     TestSuite from the given class (if the passed string is the
     *     name of an existing class) or constructs an empty TestSuite
     *     with the given name.
     *
     * @param mixed  $theClass
     * @param string $name
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function __construct($theClass = '', $name = '')
    {
        $argumentsValid = false;

        if (is_object($theClass) &&
            $theClass instanceof ReflectionClass) {
            $argumentsValid = true;
        } elseif (is_string($theClass) &&
                 $theClass !== '' &&
                 class_exists($theClass, false)) {
            $argumentsValid = true;

            if ($name == '') {
                $name = $theClass;
            }

            $theClass = new ReflectionClass($theClass);
        } elseif (is_string($theClass)) {
            $this->setName($theClass);

            return;
        }

        if (!$argumentsValid) {
            throw new PHPUnit_Framework_Exception;
        }

        if (!$theClass->isSubclassOf('PHPUnit_Framework_TestCase')) {
            throw new PHPUnit_Framework_Exception(
                'Class "' . $theClass->name . '" does not extend PHPUnit_Framework_TestCase.'
            );
        }

        if ($name != '') {
            $this->setName($name);
        } else {
            $this->setName($theClass->getName());
        }

        $constructor = $theClass->getConstructor();

        if ($constructor !== null &&
            !$constructor->isPublic()) {
            $this->addTest(
                self::warning(
                    sprintf(
                        'Class "%s" has no public constructor.',
                        $theClass->getName()
                    )
                )
            );

            return;
        }

        foreach ($theClass->getMethods() as $method) {
            $this->addTestMethod($theClass, $method);
        }

        if (empty($this->tests)) {
            $this->addTest(
                self::warning(
                    sprintf(
                        'No tests found in class "%s".',
                        $theClass->getName()
                    )
                )
            );
        }

        $this->testCase = true;
    }

    /**
     * Returns a string representation of the test suite.
     *
     * @return string
     */
    public function toString()
    {
        return $this->getName();
    }

    /**
     * Adds a test to the suite.
     *
     * @param PHPUnit_Framework_Test $test
     * @param array                  $groups
     */
    public function addTest(PHPUnit_Framework_Test $test, $groups = [])
    {
        $class = new ReflectionClass($test);

        if (!$class->isAbstract()) {
            $this->tests[]  = $test;
            $this->numTests = -1;

            if ($test instanceof self &&
                empty($groups)) {
                $groups = $test->getGroups();
            }

            if (empty($groups)) {
                $groups = ['default'];
            }

            foreach ($groups as $group) {
                if (!isset($this->groups[$group])) {
                    $this->groups[$group] = [$test];
                } else {
                    $this->groups[$group][] = $test;
                }
            }

            if ($test instanceof PHPUnit_Framework_TestCase) {
                $test->setGroups($groups);
            }
        }
    }

    /**
     * Adds the tests from the given class to the suite.
     *
     * @param mixed $testClass
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function addTestSuite($testClass)
    {
        if (is_string($testClass) && class_exists($testClass)) {
            $testClass = new ReflectionClass($testClass);
        }

        if (!is_object($testClass)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(
                1,
                'class name or object'
            );
        }

        if ($testClass instanceof self) {
            $this->addTest($testClass);
        } elseif ($testClass instanceof ReflectionClass) {
            $suiteMethod = false;

            if (!$testClass->isAbstract()) {
                if ($testClass->hasMethod(PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME)) {
                    $method = $testClass->getMethod(
                        PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME
                    );

                    if ($method->isStatic()) {
                        $this->addTest(
                            $method->invoke(null, $testClass->getName())
                        );

                        $suiteMethod = true;
                    }
                }
            }

            if (!$suiteMethod && !$testClass->isAbstract()) {
                $this->addTest(new self($testClass));
            }
        } else {
            throw new PHPUnit_Framework_Exception;
        }
    }

    /**
     * Wraps both <code>addTest()</code> and <code>addTestSuite</code>
     * as well as the separate import statements for the user's convenience.
     *
     * If the named file cannot be read or there are no new tests that can be
     * added, a <code>PHPUnit_Framework_WarningTestCase</code> will be created instead,
     * leaving the current test run untouched.
     *
     * @param string $filename
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function addTestFile($filename)
    {
        if (!is_string($filename)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (file_exists($filename) && substr($filename, -5) == '.phpt') {
            $this->addTest(
                new PHPUnit_Extensions_PhptTestCase($filename)
            );

            return;
        }

        // The given file may contain further stub classes in addition to the
        // test class itself. Figure out the actual test class.
        $classes    = get_declared_classes();
        $filename   = PHPUnit_Util_Fileloader::checkAndLoad($filename);
        $newClasses = array_diff(get_declared_classes(), $classes);

        // The diff is empty in case a parent class (with test methods) is added
        // AFTER a child class that inherited from it. To account for that case,
        // cumulate all discovered classes, so the parent class may be found in
        // a later invocation.
        if (!empty($newClasses)) {
            // On the assumption that test classes are defined first in files,
            // process discovered classes in approximate LIFO order, so as to
            // avoid unnecessary reflection.
            $this->foundClasses = array_merge($newClasses, $this->foundClasses);
        }

        // The test class's name must match the filename, either in full, or as
        // a PEAR/PSR-0 prefixed shortname ('NameSpace_ShortName'), or as a
        // PSR-1 local shortname ('NameSpace\ShortName'). The comparison must be
        // anchored to prevent false-positive matches (e.g., 'OtherShortName').
        $shortname      = basename($filename, '.php');
        $shortnameRegEx = '/(?:^|_|\\\\)' . preg_quote($shortname, '/') . '$/';

        foreach ($this->foundClasses as $i => $className) {
            if (preg_match($shortnameRegEx, $className)) {
                $class = new ReflectionClass($className);

                if ($class->getFileName() == $filename) {
                    $newClasses = [$className];
                    unset($this->foundClasses[$i]);
                    break;
                }
            }
        }

        foreach ($newClasses as $className) {
            $class = new ReflectionClass($className);

            if (!$class->isAbstract()) {
                if ($class->hasMethod(PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME)) {
                    $method = $class->getMethod(
                        PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME
                    );

                    if ($method->isStatic()) {
                        $this->addTest($method->invoke(null, $className));
                    }
                } elseif ($class->implementsInterface('PHPUnit_Framework_Test')) {
                    $this->addTestSuite($class);
                }
            }
        }

        $this->numTests = -1;
    }

    /**
     * Wrapper for addTestFile() that adds multiple test files.
     *
     * @param array|Iterator $filenames
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function addTestFiles($filenames)
    {
        if (!(is_array($filenames) ||
             (is_object($filenames) && $filenames instanceof Iterator))) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(
                1,
                'array or iterator'
            );
        }

        foreach ($filenames as $filename) {
            $this->addTestFile((string) $filename);
        }
    }

    /**
     * Counts the number of test cases that will be run by this test.
     *
     * @param bool $preferCache Indicates if cache is preferred.
     *
     * @return int
     */
    public function count($preferCache = false)
    {
        if ($preferCache && $this->cachedNumTests !== null) {
            $numTests = $this->cachedNumTests;
        } else {
            $numTests = 0;

            foreach ($this as $test) {
                $numTests += count($test);
            }

            $this->cachedNumTests = $numTests;
        }

        return $numTests;
    }

    /**
     * @param ReflectionClass $theClass
     * @param string          $name
     *
     * @return PHPUnit_Framework_Test
     *
     * @throws PHPUnit_Framework_Exception
     */
    public static function createTest(ReflectionClass $theClass, $name)
    {
        $className = $theClass->getName();

        if (!$theClass->isInstantiable()) {
            return self::warning(
                sprintf('Cannot instantiate class "%s".', $className)
            );
        }

        $backupSettings = PHPUnit_Util_Test::getBackupSettings(
            $className,
            $name
        );

        $preserveGlobalState = PHPUnit_Util_Test::getPreserveGlobalStateSettings(
            $className,
            $name
        );

        $runTestInSeparateProcess = PHPUnit_Util_Test::getProcessIsolationSettings(
            $className,
            $name
        );

        $constructor = $theClass->getConstructor();

        if ($constructor !== null) {
            $parameters = $constructor->getParameters();

            // TestCase() or TestCase($name)
            if (count($parameters) < 2) {
                $test = new $className;
            } // TestCase($name, $data)
            else {
                try {
                    $data = PHPUnit_Util_Test::getProvidedData(
                        $className,
                        $name
                    );
                } catch (PHPUnit_Framework_IncompleteTestError $e) {
                    $message = sprintf(
                        'Test for %s::%s marked incomplete by data provider',
                        $className,
                        $name
                    );

                    $_message = $e->getMessage();

                    if (!empty($_message)) {
                        $message .= "\n" . $_message;
                    }

                    $data = self::incompleteTest($className, $name, $message);
                } catch (PHPUnit_Framework_SkippedTestError $e) {
                    $message = sprintf(
                        'Test for %s::%s skipped by data provider',
                        $className,
                        $name
                    );

                    $_message = $e->getMessage();

                    if (!empty($_message)) {
                        $message .= "\n" . $_message;
                    }

                    $data = self::skipTest($className, $name, $message);
                } catch (Throwable $_t) {
                    $t = $_t;
                } catch (Exception $_t) {
                    $t = $_t;
                }

                if (isset($t)) {
                    $message = sprintf(
                        'The data provider specified for %s::%s is invalid.',
                        $className,
                        $name
                    );

                    $_message = $t->getMessage();

                    if (!empty($_message)) {
                        $message .= "\n" . $_message;
                    }

                    $data = self::warning($message);
                }

                // Test method with @dataProvider.
                if (isset($data)) {
                    $test = new PHPUnit_Framework_TestSuite_DataProvider(
                        $className . '::' . $name
                    );

                    if (empty($data)) {
                        $data = self::warning(
                            sprintf(
                                'No tests found in suite "%s".',
                                $test->getName()
                            )
                        );
                    }

                    $groups = PHPUnit_Util_Test::getGroups($className, $name);

                    if ($data instanceof PHPUnit_Framework_WarningTestCase ||
                        $data instanceof PHPUnit_Framework_SkippedTestCase ||
                        $data instanceof PHPUnit_Framework_IncompleteTestCase) {
                        $test->addTest($data, $groups);
                    } else {
                        foreach ($data as $_dataName => $_data) {
                            $_test = new $className($name, $_data, $_dataName);

                            if ($runTestInSeparateProcess) {
                                $_test->setRunTestInSeparateProcess(true);

                                if ($preserveGlobalState !== null) {
                                    $_test->setPreserveGlobalState($preserveGlobalState);
                                }
                            }

                            if ($backupSettings['backupGlobals'] !== null) {
                                $_test->setBackupGlobals(
                                    $backupSettings['backupGlobals']
                                );
                            }

                            if ($backupSettings['backupStaticAttributes'] !== null) {
                                $_test->setBackupStaticAttributes(
                                    $backupSettings['backupStaticAttributes']
                                );
                            }

                            $test->addTest($_test, $groups);
                        }
                    }
                } else {
                    $test = new $className;
                }
            }
        }

        if (!isset($test)) {
            throw new PHPUnit_Framework_Exception('No valid test provided.');
        }

        if ($test instanceof PHPUnit_Framework_TestCase) {
            $test->setName($name);

            if ($runTestInSeparateProcess) {
                $test->setRunTestInSeparateProcess(true);

                if ($preserveGlobalState !== null) {
                    $test->setPreserveGlobalState($preserveGlobalState);
                }
            }

            if ($backupSettings['backupGlobals'] !== null) {
                $test->setBackupGlobals($backupSettings['backupGlobals']);
            }

            if ($backupSettings['backupStaticAttributes'] !== null) {
                $test->setBackupStaticAttributes(
                    $backupSettings['backupStaticAttributes']
                );
            }
        }

        return $test;
    }

    /**
     * Creates a default TestResult object.
     *
     * @return PHPUnit_Framework_TestResult
     */
    protected function createResult()
    {
        return new PHPUnit_Framework_TestResult;
    }

    /**
     * Returns the name of the suite.
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Returns the test groups of the suite.
     *
     * @return array
     */
    public function getGroups()
    {
        return array_keys($this->groups);
    }

    public function getGroupDetails()
    {
        return $this->groups;
    }

    /**
     * Set tests groups of the test case
     *
     * @param array $groups
     */
    public function setGroupDetails(array $groups)
    {
        $this->groups = $groups;
    }

    /**
     * Runs the tests and collects their result in a TestResult.
     *
     * @param PHPUnit_Framework_TestResult $result
     *
     * @return PHPUnit_Framework_TestResult
     */
    public function run(PHPUnit_Framework_TestResult $result = null)
    {
        if ($result === null) {
            $result = $this->createResult();
        }

        if (count($this) == 0) {
            return $result;
        }

        $hookMethods = PHPUnit_Util_Test::getHookMethods($this->name);

        $result->startTestSuite($this);

        try {
            $this->setUp();

            foreach ($hookMethods['beforeClass'] as $beforeClassMethod) {
                if ($this->testCase === true &&
                    class_exists($this->name, false) &&
                    method_exists($this->name, $beforeClassMethod)) {
                    if ($missingRequirements = PHPUnit_Util_Test::getMissingRequirements($this->name, $beforeClassMethod)) {
                        $this->markTestSuiteSkipped(implode(PHP_EOL, $missingRequirements));
                    }

                    call_user_func([$this->name, $beforeClassMethod]);
                }
            }
        } catch (PHPUnit_Framework_SkippedTestSuiteError $e) {
            $numTests = count($this);

            for ($i = 0; $i < $numTests; $i++) {
                $result->startTest($this);
                $result->addFailure($this, $e, 0);
                $result->endTest($this, 0);
            }

            $this->tearDown();
            $result->endTestSuite($this);

            return $result;
        } catch (Throwable $_t) {
            $t = $_t;
        } catch (Exception $_t) {
            $t = $_t;
        }

        if (isset($t)) {
            $numTests = count($this);

            for ($i = 0; $i < $numTests; $i++) {
                if ($result->shouldStop()) {
                    break;
                }

                $result->startTest($this);
                $result->addError($this, $t, 0);
                $result->endTest($this, 0);
            }

            $this->tearDown();
            $result->endTestSuite($this);

            return $result;
        }

        foreach ($this as $test) {
            if ($result->shouldStop()) {
                break;
            }

            if ($test instanceof PHPUnit_Framework_TestCase ||
                $test instanceof self) {
                $test->setbeStrictAboutChangesToGlobalState($this->beStrictAboutChangesToGlobalState);
                $test->setBackupGlobals($this->backupGlobals);
                $test->setBackupStaticAttributes($this->backupStaticAttributes);
                $test->setRunTestInSeparateProcess($this->runTestInSeparateProcess);
            }

            $test->run($result);
        }

        foreach ($hookMethods['afterClass'] as $afterClassMethod) {
            if ($this->testCase === true && class_exists($this->name, false) && method_exists($this->name, $afterClassMethod)) {
                call_user_func([$this->name, $afterClassMethod]);
            }
        }

        $this->tearDown();

        $result->endTestSuite($this);

        return $result;
    }

    /**
     * @param bool $runTestInSeparateProcess
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function setRunTestInSeparateProcess($runTestInSeparateProcess)
    {
        if (is_bool($runTestInSeparateProcess)) {
            $this->runTestInSeparateProcess = $runTestInSeparateProcess;
        } else {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }
    }

    /**
     * Runs a test.
     *
     * @deprecated
     *
     * @param PHPUnit_Framework_Test       $test
     * @param PHPUnit_Framework_TestResult $result
     */
    public function runTest(PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result)
    {
        $test->run($result);
    }

    /**
     * Sets the name of the suite.
     *
     * @param  string
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Returns the test at the given index.
     *
     * @param  int|false
     *
     * @return PHPUnit_Framework_Test
     */
    public function testAt($index)
    {
        if (isset($this->tests[$index])) {
            return $this->tests[$index];
        } else {
            return false;
        }
    }

    /**
     * Returns the tests as an enumeration.
     *
     * @return array
     */
    public function tests()
    {
        return $this->tests;
    }

    /**
     * Set tests of the test suite
     *
     * @param array $tests
     */
    public function setTests(array $tests)
    {
        $this->tests = $tests;
    }

    /**
     * Mark the test suite as skipped.
     *
     * @param string $message
     *
     * @throws PHPUnit_Framework_SkippedTestSuiteError
     */
    public function markTestSuiteSkipped($message = '')
    {
        throw new PHPUnit_Framework_SkippedTestSuiteError($message);
    }

    /**
     * @param ReflectionClass  $class
     * @param ReflectionMethod $method
     */
    protected function addTestMethod(ReflectionClass $class, ReflectionMethod $method)
    {
        if (!$this->isTestMethod($method)) {
            return;
        }

        $name = $method->getName();

        if (!$method->isPublic()) {
            $this->addTest(
                self::warning(
                    sprintf(
                        'Test method "%s" in test class "%s" is not public.',
                        $name,
                        $class->getName()
                    )
                )
            );

            return;
        }

        $test = self::createTest($class, $name);

        if ($test instanceof PHPUnit_Framework_TestCase ||
            $test instanceof PHPUnit_Framework_TestSuite_DataProvider) {
            $test->setDependencies(
                PHPUnit_Util_Test::getDependencies($class->getName(), $name)
            );
        }

        $this->addTest(
            $test,
            PHPUnit_Util_Test::getGroups($class->getName(), $name)
        );
    }

    /**
     * @param ReflectionMethod $method
     *
     * @return bool
     */
    public static function isTestMethod(ReflectionMethod $method)
    {
        if (strpos($method->name, 'test') === 0) {
            return true;
        }

        // @scenario on TestCase::testMethod()
        // @test     on TestCase::testMethod()
        $docComment = $method->getDocComment();

        return strpos($docComment, '@test') !== false ||
               strpos($docComment, '@scenario') !== false;
    }

    /**
     * @param string $message
     *
     * @return PHPUnit_Framework_WarningTestCase
     */
    protected static function warning($message)
    {
        return new PHPUnit_Framework_WarningTestCase($message);
    }

    /**
     * @param string $class
     * @param string $methodName
     * @param string $message
     *
     * @return PHPUnit_Framework_SkippedTestCase
     */
    protected static function skipTest($class, $methodName, $message)
    {
        return new PHPUnit_Framework_SkippedTestCase($class, $methodName, $message);
    }

    /**
     * @param string $class
     * @param string $methodName
     * @param string $message
     *
     * @return PHPUnit_Framework_IncompleteTestCase
     */
    protected static function incompleteTest($class, $methodName, $message)
    {
        return new PHPUnit_Framework_IncompleteTestCase($class, $methodName, $message);
    }

    /**
     * @param bool $beStrictAboutChangesToGlobalState
     */
    public function setbeStrictAboutChangesToGlobalState($beStrictAboutChangesToGlobalState)
    {
        if (is_null($this->beStrictAboutChangesToGlobalState) && is_bool($beStrictAboutChangesToGlobalState)) {
            $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState;
        }
    }

    /**
     * @param bool $backupGlobals
     */
    public function setBackupGlobals($backupGlobals)
    {
        if (is_null($this->backupGlobals) && is_bool($backupGlobals)) {
            $this->backupGlobals = $backupGlobals;
        }
    }

    /**
     * @param bool $backupStaticAttributes
     */
    public function setBackupStaticAttributes($backupStaticAttributes)
    {
        if (is_null($this->backupStaticAttributes) &&
            is_bool($backupStaticAttributes)) {
            $this->backupStaticAttributes = $backupStaticAttributes;
        }
    }

    /**
     * Returns an iterator for this test suite.
     *
     * @return RecursiveIteratorIterator
     */
    public function getIterator()
    {
        $iterator = new PHPUnit_Util_TestSuiteIterator($this);

        if ($this->iteratorFilter !== null) {
            $iterator = $this->iteratorFilter->factory($iterator, $this);
        }

        return $iterator;
    }

    public function injectFilter(PHPUnit_Runner_Filter_Factory $filter)
    {
        $this->iteratorFilter = $filter;
        foreach ($this as $test) {
            if ($test instanceof self) {
                $test->injectFilter($filter);
            }
        }
    }

    /**
     * Template Method that is called before the tests
     * of this test suite are run.
     */
    protected function setUp()
    {
    }

    /**
     * Template Method that is called after the tests
     * of this test suite have finished running.
     */
    protected function tearDown()
    {
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Extension to PHPUnit_Framework_AssertionFailedError to mark the special
 * case of a test that unintentionally covers code.
 */
class PHPUnit_Framework_UnintentionallyCoveredCodeError extends PHPUnit_Framework_RiskyTestError
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Thrown when an there is a warning.
 */
class PHPUnit_Framework_Warning extends PHPUnit_Framework_Exception implements PHPUnit_Framework_SelfDescribing
{
    /**
     * Wrapper for getMessage() which is declared as final.
     *
     * @return string
     */
    public function toString()
    {
        return $this->getMessage();
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A warning.
 */
class PHPUnit_Framework_WarningTestCase extends PHPUnit_Framework_TestCase
{
    /**
     * @var string
     */
    protected $message = '';

    /**
     * @var bool
     */
    protected $backupGlobals = false;

    /**
     * @var bool
     */
    protected $backupStaticAttributes = false;

    /**
     * @var bool
     */
    protected $runTestInSeparateProcess = false;

    /**
     * @var bool
     */
    protected $useErrorHandler = false;

    /**
     * @param string $message
     */
    public function __construct($message = '')
    {
        $this->message = $message;
        parent::__construct('Warning');
    }

    /**
     * @throws PHPUnit_Framework_Exception
     */
    protected function runTest()
    {
        throw new PHPUnit_Framework_Warning($this->message);
    }

    /**
     * @return string
     */
    public function getMessage()
    {
        return $this->message;
    }

    /**
     * Returns a string representation of the test case.
     *
     * @return string
     */
    public function toString()
    {
        return 'Warning';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Base class for all test runners.
 */
abstract class PHPUnit_Runner_BaseTestRunner
{
    const STATUS_PASSED     = 0;
    const STATUS_SKIPPED    = 1;
    const STATUS_INCOMPLETE = 2;
    const STATUS_FAILURE    = 3;
    const STATUS_ERROR      = 4;
    const STATUS_RISKY      = 5;
    const STATUS_WARNING    = 6;
    const SUITE_METHODNAME  = 'suite';

    /**
     * Returns the loader to be used.
     *
     * @return PHPUnit_Runner_TestSuiteLoader
     */
    public function getLoader()
    {
        return new PHPUnit_Runner_StandardTestSuiteLoader;
    }

    /**
     * Returns the Test corresponding to the given suite.
     * This is a template method, subclasses override
     * the runFailed() and clearStatus() methods.
     *
     * @param string $suiteClassName
     * @param string $suiteClassFile
     * @param mixed  $suffixes
     *
     * @return PHPUnit_Framework_Test
     */
    public function getTest($suiteClassName, $suiteClassFile = '', $suffixes = '')
    {
        if (is_dir($suiteClassName) &&
            !is_file($suiteClassName . '.php') && empty($suiteClassFile)) {
            $facade = new File_Iterator_Facade;
            $files  = $facade->getFilesAsArray(
                $suiteClassName,
                $suffixes
            );

            $suite = new PHPUnit_Framework_TestSuite($suiteClassName);
            $suite->addTestFiles($files);

            return $suite;
        }

        try {
            $testClass = $this->loadSuiteClass(
                $suiteClassName,
                $suiteClassFile
            );
        } catch (PHPUnit_Framework_Exception $e) {
            $this->runFailed($e->getMessage());

            return;
        }

        try {
            $suiteMethod = $testClass->getMethod(self::SUITE_METHODNAME);

            if (!$suiteMethod->isStatic()) {
                $this->runFailed(
                    'suite() method must be static.'
                );

                return;
            }

            try {
                $test = $suiteMethod->invoke(null, $testClass->getName());
            } catch (ReflectionException $e) {
                $this->runFailed(
                    sprintf(
                        "Failed to invoke suite() method.\n%s",
                        $e->getMessage()
                    )
                );

                return;
            }
        } catch (ReflectionException $e) {
            try {
                $test = new PHPUnit_Framework_TestSuite($testClass);
            } catch (PHPUnit_Framework_Exception $e) {
                $test = new PHPUnit_Framework_TestSuite;
                $test->setName($suiteClassName);
            }
        }

        $this->clearStatus();

        return $test;
    }

    /**
     * Returns the loaded ReflectionClass for a suite name.
     *
     * @param string $suiteClassName
     * @param string $suiteClassFile
     *
     * @return ReflectionClass
     */
    protected function loadSuiteClass($suiteClassName, $suiteClassFile = '')
    {
        $loader = $this->getLoader();

        return $loader->load($suiteClassName, $suiteClassFile);
    }

    /**
     * Clears the status message.
     */
    protected function clearStatus()
    {
    }

    /**
     * Override to define how to handle a failed loading of
     * a test suite.
     *
     * @param string $message
     */
    abstract protected function runFailed($message);
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Runner_Exception extends RuntimeException implements PHPUnit_Exception
{
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Runner_Filter_Factory
{
    /**
     * @var array
     */
    private $filters = [];

    /**
     * @param ReflectionClass $filter
     * @param mixed           $args
     */
    public function addFilter(ReflectionClass $filter, $args)
    {
        if (!$filter->isSubclassOf('RecursiveFilterIterator')) {
            throw new InvalidArgumentException(
                sprintf(
                    'Class "%s" does not extend RecursiveFilterIterator',
                    $filter->name
                )
            );
        }

        $this->filters[] = [$filter, $args];
    }

    /**
     * @return FilterIterator
     */
    public function factory(Iterator $iterator, PHPUnit_Framework_TestSuite $suite)
    {
        foreach ($this->filters as $filter) {
            list($class, $args) = $filter;
            $iterator           = $class->newInstance($iterator, $args, $suite);
        }

        return $iterator;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Runner_Filter_Group_Exclude extends PHPUnit_Runner_Filter_GroupFilterIterator
{
    /**
     * @param string $hash
     *
     * @return bool
     */
    protected function doAccept($hash)
    {
        return !in_array($hash, $this->groupTests);
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Runner_Filter_Group_Include extends PHPUnit_Runner_Filter_GroupFilterIterator
{
    /**
     * @param string $hash
     *
     * @return bool
     */
    protected function doAccept($hash)
    {
        return in_array($hash, $this->groupTests);
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

abstract class PHPUnit_Runner_Filter_GroupFilterIterator extends RecursiveFilterIterator
{
    /**
     * @var array
     */
    protected $groupTests = [];

    /**
     * @param RecursiveIterator           $iterator
     * @param array                       $groups
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function __construct(RecursiveIterator $iterator, array $groups, PHPUnit_Framework_TestSuite $suite)
    {
        parent::__construct($iterator);

        foreach ($suite->getGroupDetails() as $group => $tests) {
            if (in_array($group, $groups)) {
                $testHashes = array_map(
                    function ($test) {
                        return spl_object_hash($test);
                    },
                    $tests
                );

                $this->groupTests = array_merge($this->groupTests, $testHashes);
            }
        }
    }

    /**
     * @return bool
     */
    public function accept()
    {
        $test = $this->getInnerIterator()->current();

        if ($test instanceof PHPUnit_Framework_TestSuite) {
            return true;
        }

        return $this->doAccept(spl_object_hash($test));
    }

    abstract protected function doAccept($hash);
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Runner_Filter_Test extends RecursiveFilterIterator
{
    /**
     * @var string
     */
    protected $filter = null;

    /**
     * @var int
     */
    protected $filterMin;
    /**
     * @var int
     */
    protected $filterMax;

    /**
     * @param RecursiveIterator $iterator
     * @param string            $filter
     */
    public function __construct(RecursiveIterator $iterator, $filter)
    {
        parent::__construct($iterator);
        $this->setFilter($filter);
    }

    /**
     * @param string $filter
     */
    protected function setFilter($filter)
    {
        if (PHPUnit_Util_Regex::pregMatchSafe($filter, '') === false) {
            // Handles:
            //  * testAssertEqualsSucceeds#4
            //  * testAssertEqualsSucceeds#4-8
            if (preg_match('/^(.*?)#(\d+)(?:-(\d+))?$/', $filter, $matches)) {
                if (isset($matches[3]) && $matches[2] < $matches[3]) {
                    $filter = sprintf(
                        '%s.*with data set #(\d+)$',
                        $matches[1]
                    );

                    $this->filterMin = $matches[2];
                    $this->filterMax = $matches[3];
                } else {
                    $filter = sprintf(
                        '%s.*with data set #%s$',
                        $matches[1],
                        $matches[2]
                    );
                }
            } // Handles:
            //  * testDetermineJsonError@JSON_ERROR_NONE
            //  * testDetermineJsonError@JSON.*
            elseif (preg_match('/^(.*?)@(.+)$/', $filter, $matches)) {
                $filter = sprintf(
                    '%s.*with data set "%s"$',
                    $matches[1],
                    $matches[2]
                );
            }

            // Escape delimiters in regular expression. Do NOT use preg_quote,
            // to keep magic characters.
            $filter = sprintf('/%s/', str_replace(
                '/',
                '\\/',
                $filter
            ));
        }

        $this->filter = $filter;
    }

    /**
     * @return bool
     */
    public function accept()
    {
        $test = $this->getInnerIterator()->current();

        if ($test instanceof PHPUnit_Framework_TestSuite) {
            return true;
        }

        if ($test instanceof PHPUnit_Framework_WarningTestCase) {
            $name = $test->getMessage();
        } else {
            $tmp = PHPUnit_Util_Test::describe($test, false);

            if ($tmp[0] != '') {
                $name = implode('::', $tmp);
            } else {
                $name = $tmp[1];
            }
        }

        $accepted = @preg_match($this->filter, $name, $matches);

        if ($accepted && isset($this->filterMax)) {
            $set      = end($matches);
            $accepted = $set >= $this->filterMin && $set <= $this->filterMax;
        }

        return $accepted;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * The standard test suite loader.
 */
class PHPUnit_Runner_StandardTestSuiteLoader implements PHPUnit_Runner_TestSuiteLoader
{
    /**
     * @param string $suiteClassName
     * @param string $suiteClassFile
     *
     * @return ReflectionClass
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function load($suiteClassName, $suiteClassFile = '')
    {
        $suiteClassName = str_replace('.php', '', $suiteClassName);

        if (empty($suiteClassFile)) {
            $suiteClassFile = PHPUnit_Util_Filesystem::classNameToFilename(
                $suiteClassName
            );
        }

        if (!class_exists($suiteClassName, false)) {
            $loadedClasses = get_declared_classes();

            $filename = PHPUnit_Util_Fileloader::checkAndLoad($suiteClassFile);

            $loadedClasses = array_values(
                array_diff(get_declared_classes(), $loadedClasses)
            );
        }

        if (!class_exists($suiteClassName, false) && !empty($loadedClasses)) {
            $offset = 0 - strlen($suiteClassName);

            foreach ($loadedClasses as $loadedClass) {
                $class = new ReflectionClass($loadedClass);
                if (substr($loadedClass, $offset) === $suiteClassName &&
                    $class->getFileName() == $filename) {
                    $suiteClassName = $loadedClass;
                    break;
                }
            }
        }

        if (!class_exists($suiteClassName, false) && !empty($loadedClasses)) {
            $testCaseClass = 'PHPUnit_Framework_TestCase';

            foreach ($loadedClasses as $loadedClass) {
                $class     = new ReflectionClass($loadedClass);
                $classFile = $class->getFileName();

                if ($class->isSubclassOf($testCaseClass) &&
                    !$class->isAbstract()) {
                    $suiteClassName = $loadedClass;
                    $testCaseClass  = $loadedClass;

                    if ($classFile == realpath($suiteClassFile)) {
                        break;
                    }
                }

                if ($class->hasMethod('suite')) {
                    $method = $class->getMethod('suite');

                    if (!$method->isAbstract() &&
                        $method->isPublic() &&
                        $method->isStatic()) {
                        $suiteClassName = $loadedClass;

                        if ($classFile == realpath($suiteClassFile)) {
                            break;
                        }
                    }
                }
            }
        }

        if (class_exists($suiteClassName, false)) {
            $class = new ReflectionClass($suiteClassName);

            if ($class->getFileName() == realpath($suiteClassFile)) {
                return $class;
            }
        }

        throw new PHPUnit_Framework_Exception(
            sprintf(
                "Class '%s' could not be found in '%s'.",
                $suiteClassName,
                $suiteClassFile
            )
        );
    }

    /**
     * @param ReflectionClass $aClass
     *
     * @return ReflectionClass
     */
    public function reload(ReflectionClass $aClass)
    {
        return $aClass;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * An interface to define how a test suite should be loaded.
 */
interface PHPUnit_Runner_TestSuiteLoader
{
    /**
     * @param string $suiteClassName
     * @param string $suiteClassFile
     *
     * @return ReflectionClass
     */
    public function load($suiteClassName, $suiteClassFile = '');

    /**
     * @param ReflectionClass $aClass
     *
     * @return ReflectionClass
     */
    public function reload(ReflectionClass $aClass);
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use SebastianBergmann\Version;

/**
 * This class defines the current version of PHPUnit.
 */
class PHPUnit_Runner_Version
{
    private static $pharVersion;
    private static $version;

    /**
     * Returns the current version of PHPUnit.
     *
     * @return string
     */
    public static function id()
    {
        if (self::$pharVersion !== null) {
            return self::$pharVersion;
        }

        if (self::$version === null) {
            $version       = new Version('5.7.21', dirname(dirname(__DIR__)));
            self::$version = $version->getVersion();
        }

        return self::$version;
    }

    /**
     * @return string
     */
    public static function series()
    {
        if (strpos(self::id(), '-')) {
            $version = explode('-', self::id())[0];
        } else {
            $version = self::id();
        }

        return implode('.', array_slice(explode('.', $version), 0, 2));
    }

    /**
     * @return string
     */
    public static function getVersionString()
    {
        return 'PHPUnit ' . self::id() . ' by Sebastian Bergmann and contributors.';
    }

    /**
     * @return string
     */
    public static function getReleaseChannel()
    {
        if (strpos(self::$pharVersion, '-') !== false) {
            return '-nightly';
        }

        return '';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A TestRunner for the Command Line Interface (CLI)
 * PHP SAPI Module.
 */
class PHPUnit_TextUI_Command
{
    /**
     * @var array
     */
    protected $arguments = [
        'listGroups'              => false,
        'listSuites'              => false,
        'loader'                  => null,
        'useDefaultConfiguration' => true,
        'loadedExtensions'        => [],
        'notLoadedExtensions'     => []
    ];

    /**
     * @var array
     */
    protected $options = [];

    /**
     * @var array
     */
    protected $longOptions = [
        'atleast-version='        => null,
        'bootstrap='              => null,
        'colors=='                => null,
        'columns='                => null,
        'configuration='          => null,
        'coverage-clover='        => null,
        'coverage-crap4j='        => null,
        'coverage-html='          => null,
        'coverage-php='           => null,
        'coverage-text=='         => null,
        'coverage-xml='           => null,
        'debug'                   => null,
        'disallow-test-output'    => null,
        'disallow-resource-usage' => null,
        'disallow-todo-tests'     => null,
        'enforce-time-limit'      => null,
        'exclude-group='          => null,
        'filter='                 => null,
        'generate-configuration'  => null,
        'group='                  => null,
        'help'                    => null,
        'include-path='           => null,
        'list-groups'             => null,
        'list-suites'             => null,
        'loader='                 => null,
        'log-json='               => null,
        'log-junit='              => null,
        'log-tap='                => null,
        'log-teamcity='           => null,
        'no-configuration'        => null,
        'no-coverage'             => null,
        'no-extensions'           => null,
        'no-globals-backup'       => null,
        'printer='                => null,
        'process-isolation'       => null,
        'repeat='                 => null,
        'report-useless-tests'    => null,
        'reverse-list'            => null,
        'static-backup'           => null,
        'stderr'                  => null,
        'stop-on-error'           => null,
        'stop-on-failure'         => null,
        'stop-on-warning'         => null,
        'stop-on-incomplete'      => null,
        'stop-on-risky'           => null,
        'stop-on-skipped'         => null,
        'fail-on-warning'         => null,
        'fail-on-risky'           => null,
        'strict-coverage'         => null,
        'disable-coverage-ignore' => null,
        'strict-global-state'     => null,
        'tap'                     => null,
        'teamcity'                => null,
        'testdox'                 => null,
        'testdox-group='          => null,
        'testdox-exclude-group='  => null,
        'testdox-html='           => null,
        'testdox-text='           => null,
        'testdox-xml='            => null,
        'test-suffix='            => null,
        'testsuite='              => null,
        'verbose'                 => null,
        'version'                 => null,
        'whitelist='              => null
    ];

    /**
     * @var bool
     */
    private $versionStringPrinted = false;

    /**
     * @param bool $exit
     */
    public static function main($exit = true)
    {
        $command = new static;

        return $command->run($_SERVER['argv'], $exit);
    }

    /**
     * @param array $argv
     * @param bool  $exit
     *
     * @return int
     */
    public function run(array $argv, $exit = true)
    {
        $this->handleArguments($argv);

        $runner = $this->createRunner();

        if (is_object($this->arguments['test']) &&
            $this->arguments['test'] instanceof PHPUnit_Framework_Test) {
            $suite = $this->arguments['test'];
        } else {
            $suite = $runner->getTest(
                $this->arguments['test'],
                $this->arguments['testFile'],
                $this->arguments['testSuffixes']
            );
        }

        if ($this->arguments['listGroups']) {
            $this->printVersionString();

            print "Available test group(s):\n";

            $groups = $suite->getGroups();
            sort($groups);

            foreach ($groups as $group) {
                print " - $group\n";
            }

            if ($exit) {
                exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
            } else {
                return PHPUnit_TextUI_TestRunner::SUCCESS_EXIT;
            }
        }

        if ($this->arguments['listSuites']) {
            $this->printVersionString();

            print "Available test suite(s):\n";

            $configuration = PHPUnit_Util_Configuration::getInstance(
                $this->arguments['configuration']
            );

            $suiteNames = $configuration->getTestSuiteNames();
            foreach ($suiteNames as $suiteName) {
                print " - $suiteName\n";
            }

            if ($exit) {
                exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
            } else {
                return PHPUnit_TextUI_TestRunner::SUCCESS_EXIT;
            }
        }

        unset($this->arguments['test']);
        unset($this->arguments['testFile']);

        try {
            $result = $runner->doRun($suite, $this->arguments, $exit);
        } catch (PHPUnit_Framework_Exception $e) {
            print $e->getMessage() . "\n";
        }

        $return = PHPUnit_TextUI_TestRunner::FAILURE_EXIT;

        if (isset($result) && $result->wasSuccessful(false)) {
            $return = PHPUnit_TextUI_TestRunner::SUCCESS_EXIT;
        } elseif (!isset($result) || $result->errorCount() > 0) {
            $return = PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT;
        }

        if ($exit) {
            exit($return);
        }

        return $return;
    }

    /**
     * Create a TestRunner, override in subclasses.
     *
     * @return PHPUnit_TextUI_TestRunner
     */
    protected function createRunner()
    {
        return new PHPUnit_TextUI_TestRunner($this->arguments['loader']);
    }

    /**
     * Handles the command-line arguments.
     *
     * A child class of PHPUnit_TextUI_Command can hook into the argument
     * parsing by adding the switch(es) to the $longOptions array and point to a
     * callback method that handles the switch(es) in the child class like this
     *
     * <code>
     * <?php
     * class MyCommand extends PHPUnit_TextUI_Command
     * {
     *     public function __construct()
     *     {
     *         // my-switch won't accept a value, it's an on/off
     *         $this->longOptions['my-switch'] = 'myHandler';
     *         // my-secondswitch will accept a value - note the equals sign
     *         $this->longOptions['my-secondswitch='] = 'myOtherHandler';
     *     }
     *
     *     // --my-switch  -> myHandler()
     *     protected function myHandler()
     *     {
     *     }
     *
     *     // --my-secondswitch foo -> myOtherHandler('foo')
     *     protected function myOtherHandler ($value)
     *     {
     *     }
     *
     *     // You will also need this - the static keyword in the
     *     // PHPUnit_TextUI_Command will mean that it'll be
     *     // PHPUnit_TextUI_Command that gets instantiated,
     *     // not MyCommand
     *     public static function main($exit = true)
     *     {
     *         $command = new static;
     *
     *         return $command->run($_SERVER['argv'], $exit);
     *     }
     *
     * }
     * </code>
     *
     * @param array $argv
     */
    protected function handleArguments(array $argv)
    {
        if (defined('__PHPUNIT_PHAR__')) {
            $this->longOptions['check-version'] = null;
            $this->longOptions['selfupdate']    = null;
            $this->longOptions['self-update']   = null;
            $this->longOptions['selfupgrade']   = null;
            $this->longOptions['self-upgrade']  = null;
        }

        try {
            $this->options = PHPUnit_Util_Getopt::getopt(
                $argv,
                'd:c:hv',
                array_keys($this->longOptions)
            );
        } catch (PHPUnit_Framework_Exception $e) {
            $this->showError($e->getMessage());
        }

        foreach ($this->options[0] as $option) {
            switch ($option[0]) {
                case '--colors':
                    $this->arguments['colors'] = $option[1] ?: PHPUnit_TextUI_ResultPrinter::COLOR_AUTO;
                    break;

                case '--bootstrap':
                    $this->arguments['bootstrap'] = $option[1];
                    break;

                case '--columns':
                    if (is_numeric($option[1])) {
                        $this->arguments['columns'] = (int) $option[1];
                    } elseif ($option[1] == 'max') {
                        $this->arguments['columns'] = 'max';
                    }
                    break;

                case 'c':
                case '--configuration':
                    $this->arguments['configuration'] = $option[1];
                    break;

                case '--coverage-clover':
                    $this->arguments['coverageClover'] = $option[1];
                    break;

                case '--coverage-crap4j':
                    $this->arguments['coverageCrap4J'] = $option[1];
                    break;

                case '--coverage-html':
                    $this->arguments['coverageHtml'] = $option[1];
                    break;

                case '--coverage-php':
                    $this->arguments['coveragePHP'] = $option[1];
                    break;

                case '--coverage-text':
                    if ($option[1] === null) {
                        $option[1] = 'php://stdout';
                    }

                    $this->arguments['coverageText']                   = $option[1];
                    $this->arguments['coverageTextShowUncoveredFiles'] = false;
                    $this->arguments['coverageTextShowOnlySummary']    = false;
                    break;

                case '--coverage-xml':
                    $this->arguments['coverageXml'] = $option[1];
                    break;

                case 'd':
                    $ini = explode('=', $option[1]);

                    if (isset($ini[0])) {
                        if (isset($ini[1])) {
                            ini_set($ini[0], $ini[1]);
                        } else {
                            ini_set($ini[0], true);
                        }
                    }
                    break;

                case '--debug':
                    $this->arguments['debug'] = true;
                    break;

                case 'h':
                case '--help':
                    $this->showHelp();
                    exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
                    break;

                case '--filter':
                    $this->arguments['filter'] = $option[1];
                    break;

                case '--testsuite':
                    $this->arguments['testsuite'] = $option[1];
                    break;

                case '--generate-configuration':
                    $this->printVersionString();

                    printf(
                        "Generating phpunit.xml in %s\n\n",
                        getcwd()
                    );

                    print 'Bootstrap script (relative to path shown above; default: vendor/autoload.php): ';
                    $bootstrapScript = trim(fgets(STDIN));

                    print 'Tests directory (relative to path shown above; default: tests): ';
                    $testsDirectory = trim(fgets(STDIN));

                    print 'Source directory (relative to path shown above; default: src): ';
                    $src = trim(fgets(STDIN));

                    if ($bootstrapScript == '') {
                        $bootstrapScript = 'vendor/autoload.php';
                    }

                    if ($testsDirectory == '') {
                        $testsDirectory = 'tests';
                    }

                    if ($src == '') {
                        $src = 'src';
                    }

                    $generator = new PHPUnit_Util_ConfigurationGenerator;

                    file_put_contents(
                        'phpunit.xml',
                        $generator->generateDefaultConfiguration(
                            PHPUnit_Runner_Version::series(),
                            $bootstrapScript,
                            $testsDirectory,
                            $src
                        )
                    );

                    printf(
                        "\nGenerated phpunit.xml in %s\n",
                        getcwd()
                    );

                    exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
                    break;

                case '--group':
                    $this->arguments['groups'] = explode(',', $option[1]);
                    break;

                case '--exclude-group':
                    $this->arguments['excludeGroups'] = explode(
                        ',',
                        $option[1]
                    );
                    break;

                case '--test-suffix':
                    $this->arguments['testSuffixes'] = explode(
                        ',',
                        $option[1]
                    );
                    break;

                case '--include-path':
                    $includePath = $option[1];
                    break;

                case '--list-groups':
                    $this->arguments['listGroups'] = true;
                    break;

                case '--list-suites':
                    $this->arguments['listSuites'] = true;
                    break;

                case '--printer':
                    $this->arguments['printer'] = $option[1];
                    break;

                case '--loader':
                    $this->arguments['loader'] = $option[1];
                    break;

                case '--log-json':
                    $this->arguments['jsonLogfile'] = $option[1];
                    break;

                case '--log-junit':
                    $this->arguments['junitLogfile'] = $option[1];
                    break;

                case '--log-tap':
                    $this->arguments['tapLogfile'] = $option[1];
                    break;

                case '--log-teamcity':
                    $this->arguments['teamcityLogfile'] = $option[1];
                    break;

                case '--process-isolation':
                    $this->arguments['processIsolation'] = true;
                    break;

                case '--repeat':
                    $this->arguments['repeat'] = (int) $option[1];
                    break;

                case '--stderr':
                    $this->arguments['stderr'] = true;
                    break;

                case '--stop-on-error':
                    $this->arguments['stopOnError'] = true;
                    break;

                case '--stop-on-failure':
                    $this->arguments['stopOnFailure'] = true;
                    break;

                case '--stop-on-warning':
                    $this->arguments['stopOnWarning'] = true;
                    break;

                case '--stop-on-incomplete':
                    $this->arguments['stopOnIncomplete'] = true;
                    break;

                case '--stop-on-risky':
                    $this->arguments['stopOnRisky'] = true;
                    break;

                case '--stop-on-skipped':
                    $this->arguments['stopOnSkipped'] = true;
                    break;

                case '--fail-on-warning':
                    $this->arguments['failOnWarning'] = true;
                    break;

                case '--fail-on-risky':
                    $this->arguments['failOnRisky'] = true;
                    break;

                case '--tap':
                    $this->arguments['printer'] = 'PHPUnit_Util_Log_TAP';
                    break;

                case '--teamcity':
                    $this->arguments['printer'] = 'PHPUnit_Util_Log_TeamCity';
                    break;

                case '--testdox':
                    $this->arguments['printer'] = 'PHPUnit_Util_TestDox_ResultPrinter_Text';
                    break;

                case '--testdox-group':
                    $this->arguments['testdoxGroups'] = explode(
                        ',',
                        $option[1]
                    );
                    break;

                case '--testdox-exclude-group':
                    $this->arguments['testdoxExcludeGroups'] = explode(
                        ',',
                        $option[1]
                    );
                    break;

                case '--testdox-html':
                    $this->arguments['testdoxHTMLFile'] = $option[1];
                    break;

                case '--testdox-text':
                    $this->arguments['testdoxTextFile'] = $option[1];
                    break;

                case '--testdox-xml':
                    $this->arguments['testdoxXMLFile'] = $option[1];
                    break;

                case '--no-configuration':
                    $this->arguments['useDefaultConfiguration'] = false;
                    break;

                case '--no-extensions':
                    $this->arguments['noExtensions'] = true;
                    break;

                case '--no-coverage':
                    $this->arguments['noCoverage'] = true;
                    break;

                case '--no-globals-backup':
                    $this->arguments['backupGlobals'] = false;
                    break;

                case '--static-backup':
                    $this->arguments['backupStaticAttributes'] = true;
                    break;

                case 'v':
                case '--verbose':
                    $this->arguments['verbose'] = true;
                    break;

                case '--atleast-version':
                    exit(version_compare(PHPUnit_Runner_Version::id(), $option[1], '>=')
                        ? PHPUnit_TextUI_TestRunner::SUCCESS_EXIT
                        : PHPUnit_TextUI_TestRunner::FAILURE_EXIT
                    );
                    break;

                case '--version':
                    $this->printVersionString();
                    exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
                    break;

                case '--report-useless-tests':
                    $this->arguments['reportUselessTests'] = true;
                    break;

                case '--strict-coverage':
                    $this->arguments['strictCoverage'] = true;
                    break;

                case '--disable-coverage-ignore':
                    $this->arguments['disableCodeCoverageIgnore'] = true;
                    break;

                case '--strict-global-state':
                    $this->arguments['beStrictAboutChangesToGlobalState'] = true;
                    break;

                case '--disallow-test-output':
                    $this->arguments['disallowTestOutput'] = true;
                    break;

                case '--disallow-resource-usage':
                    $this->arguments['beStrictAboutResourceUsageDuringSmallTests'] = true;
                    break;

                case '--enforce-time-limit':
                    $this->arguments['enforceTimeLimit'] = true;
                    break;

                case '--disallow-todo-tests':
                    $this->arguments['disallowTodoAnnotatedTests'] = true;
                    break;

                case '--reverse-list':
                    $this->arguments['reverseList'] = true;
                    break;

                case '--check-version':
                    $this->handleVersionCheck();
                    break;

                case '--selfupdate':
                case '--self-update':
                    $this->handleSelfUpdate();
                    break;

                case '--selfupgrade':
                case '--self-upgrade':
                    $this->handleSelfUpdate(true);
                    break;

                case '--whitelist':
                    $this->arguments['whitelist'] = $option[1];
                    break;

                default:
                    $optionName = str_replace('--', '', $option[0]);

                    $handler = null;
                    if (isset($this->longOptions[$optionName])) {
                        $handler = $this->longOptions[$optionName];
                    } elseif (isset($this->longOptions[$optionName . '='])) {
                        $handler = $this->longOptions[$optionName . '='];
                    }

                    if (isset($handler) && is_callable([$this, $handler])) {
                        $this->$handler($option[1]);
                    }
            }
        }

        $this->handleCustomTestSuite();

        if (!isset($this->arguments['test'])) {
            if (isset($this->options[1][0])) {
                $this->arguments['test'] = $this->options[1][0];
            }

            if (isset($this->options[1][1])) {
                $this->arguments['testFile'] = realpath($this->options[1][1]);
            } else {
                $this->arguments['testFile'] = '';
            }

            if (isset($this->arguments['test']) &&
                is_file($this->arguments['test']) &&
                substr($this->arguments['test'], -5, 5) != '.phpt') {
                $this->arguments['testFile'] = realpath($this->arguments['test']);
                $this->arguments['test']     = substr($this->arguments['test'], 0, strrpos($this->arguments['test'], '.'));
            }
        }

        if (!isset($this->arguments['testSuffixes'])) {
            $this->arguments['testSuffixes'] = ['Test.php', '.phpt'];
        }

        if (isset($includePath)) {
            ini_set(
                'include_path',
                $includePath . PATH_SEPARATOR . ini_get('include_path')
            );
        }

        if ($this->arguments['loader'] !== null) {
            $this->arguments['loader'] = $this->handleLoader($this->arguments['loader']);
        }

        if (isset($this->arguments['configuration']) &&
            is_dir($this->arguments['configuration'])) {
            $configurationFile = $this->arguments['configuration'] . '/phpunit.xml';

            if (file_exists($configurationFile)) {
                $this->arguments['configuration'] = realpath(
                    $configurationFile
                );
            } elseif (file_exists($configurationFile . '.dist')) {
                $this->arguments['configuration'] = realpath(
                    $configurationFile . '.dist'
                );
            }
        } elseif (!isset($this->arguments['configuration']) &&
                  $this->arguments['useDefaultConfiguration']) {
            if (file_exists('phpunit.xml')) {
                $this->arguments['configuration'] = realpath('phpunit.xml');
            } elseif (file_exists('phpunit.xml.dist')) {
                $this->arguments['configuration'] = realpath(
                    'phpunit.xml.dist'
                );
            }
        }

        if (isset($this->arguments['configuration'])) {
            try {
                $configuration = PHPUnit_Util_Configuration::getInstance(
                    $this->arguments['configuration']
                );
            } catch (Throwable $e) {
                print $e->getMessage() . "\n";
                exit(PHPUnit_TextUI_TestRunner::FAILURE_EXIT);
            } catch (Exception $e) {
                print $e->getMessage() . "\n";
                exit(PHPUnit_TextUI_TestRunner::FAILURE_EXIT);
            }

            $phpunitConfiguration = $configuration->getPHPUnitConfiguration();

            $configuration->handlePHPConfiguration();

            /*
             * Issue #1216
             */
            if (isset($this->arguments['bootstrap'])) {
                $this->handleBootstrap($this->arguments['bootstrap']);
            } elseif (isset($phpunitConfiguration['bootstrap'])) {
                $this->handleBootstrap($phpunitConfiguration['bootstrap']);
            }

            /*
             * Issue #657
             */
            if (isset($phpunitConfiguration['stderr']) && ! isset($this->arguments['stderr'])) {
                $this->arguments['stderr'] = $phpunitConfiguration['stderr'];
            }

            if (isset($phpunitConfiguration['extensionsDirectory']) && !isset($this->arguments['noExtensions']) && extension_loaded('phar')) {
                $this->handleExtensions($phpunitConfiguration['extensionsDirectory']);
            }

            if (isset($phpunitConfiguration['columns']) && ! isset($this->arguments['columns'])) {
                $this->arguments['columns'] = $phpunitConfiguration['columns'];
            }

            if (!isset($this->arguments['printer']) && isset($phpunitConfiguration['printerClass'])) {
                if (isset($phpunitConfiguration['printerFile'])) {
                    $file = $phpunitConfiguration['printerFile'];
                } else {
                    $file = '';
                }

                $this->arguments['printer'] = $this->handlePrinter(
                    $phpunitConfiguration['printerClass'],
                    $file
                );
            }

            if (isset($phpunitConfiguration['testSuiteLoaderClass'])) {
                if (isset($phpunitConfiguration['testSuiteLoaderFile'])) {
                    $file = $phpunitConfiguration['testSuiteLoaderFile'];
                } else {
                    $file = '';
                }

                $this->arguments['loader'] = $this->handleLoader(
                    $phpunitConfiguration['testSuiteLoaderClass'],
                    $file
                );
            }

            if (!isset($this->arguments['test'])) {
                $testSuite = $configuration->getTestSuiteConfiguration(isset($this->arguments['testsuite']) ? $this->arguments['testsuite'] : null);

                if ($testSuite !== null) {
                    $this->arguments['test'] = $testSuite;
                }
            }
        } elseif (isset($this->arguments['bootstrap'])) {
            $this->handleBootstrap($this->arguments['bootstrap']);
        }

        if (isset($this->arguments['printer']) &&
            is_string($this->arguments['printer'])) {
            $this->arguments['printer'] = $this->handlePrinter($this->arguments['printer']);
        }

        if (isset($this->arguments['test']) && is_string($this->arguments['test']) && substr($this->arguments['test'], -5, 5) == '.phpt') {
            $test = new PHPUnit_Extensions_PhptTestCase($this->arguments['test']);

            $this->arguments['test'] = new PHPUnit_Framework_TestSuite;
            $this->arguments['test']->addTest($test);
        }

        if (!isset($this->arguments['test']) ||
            (isset($this->arguments['testDatabaseLogRevision']) && !isset($this->arguments['testDatabaseDSN']))) {
            $this->showHelp();
            exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT);
        }
    }

    /**
     * Handles the loading of the PHPUnit_Runner_TestSuiteLoader implementation.
     *
     * @param string $loaderClass
     * @param string $loaderFile
     *
     * @return PHPUnit_Runner_TestSuiteLoader
     */
    protected function handleLoader($loaderClass, $loaderFile = '')
    {
        if (!class_exists($loaderClass, false)) {
            if ($loaderFile == '') {
                $loaderFile = PHPUnit_Util_Filesystem::classNameToFilename(
                    $loaderClass
                );
            }

            $loaderFile = stream_resolve_include_path($loaderFile);

            if ($loaderFile) {
                require $loaderFile;
            }
        }

        if (class_exists($loaderClass, false)) {
            $class = new ReflectionClass($loaderClass);

            if ($class->implementsInterface('PHPUnit_Runner_TestSuiteLoader') &&
                $class->isInstantiable()) {
                return $class->newInstance();
            }
        }

        if ($loaderClass == 'PHPUnit_Runner_StandardTestSuiteLoader') {
            return;
        }

        $this->showError(
            sprintf(
                'Could not use "%s" as loader.',
                $loaderClass
            )
        );
    }

    /**
     * Handles the loading of the PHPUnit_Util_Printer implementation.
     *
     * @param string $printerClass
     * @param string $printerFile
     *
     * @return PHPUnit_Util_Printer|string
     */
    protected function handlePrinter($printerClass, $printerFile = '')
    {
        if (!class_exists($printerClass, false)) {
            if ($printerFile == '') {
                $printerFile = PHPUnit_Util_Filesystem::classNameToFilename(
                    $printerClass
                );
            }

            $printerFile = stream_resolve_include_path($printerFile);

            if ($printerFile) {
                require $printerFile;
            }
        }

        if (class_exists($printerClass)) {
            $class = new ReflectionClass($printerClass);

            if ($class->implementsInterface('PHPUnit_Framework_TestListener') &&
                $class->isSubclassOf('PHPUnit_Util_Printer') &&
                $class->isInstantiable()) {
                if ($class->isSubclassOf('PHPUnit_TextUI_ResultPrinter')) {
                    return $printerClass;
                }

                $outputStream = isset($this->arguments['stderr']) ? 'php://stderr' : null;

                return $class->newInstance($outputStream);
            }
        }

        $this->showError(
            sprintf(
                'Could not use "%s" as printer.',
                $printerClass
            )
        );
    }

    /**
     * Loads a bootstrap file.
     *
     * @param string $filename
     */
    protected function handleBootstrap($filename)
    {
        try {
            PHPUnit_Util_Fileloader::checkAndLoad($filename);
        } catch (PHPUnit_Framework_Exception $e) {
            $this->showError($e->getMessage());
        }
    }

    protected function handleSelfUpdate($upgrade = false)
    {
        $this->printVersionString();

        if ($upgrade) {
            print "Warning: Deprecated --self-upgrade used\n\n";
        } else {
            print "Warning: Deprecated --self-update used\n\n";
        }

        $localFilename = realpath($_SERVER['argv'][0]);

        if (!is_writable($localFilename)) {
            print 'No write permission to update ' . $localFilename . "\n";
            exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT);
        }

        if (!extension_loaded('openssl')) {
            print "The OpenSSL extension is not loaded.\n";
            exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT);
        }

        if (!$upgrade) {
            $remoteFilename = sprintf(
                'https://phar.phpunit.de/phpunit-%s.phar',
                file_get_contents(
                    sprintf(
                        'https://phar.phpunit.de/latest-version-of/phpunit-%s',
                        PHPUnit_Runner_Version::series()
                    )
                )
            );
        } else {
            $remoteFilename = sprintf(
                'https://phar.phpunit.de/phpunit%s.phar',
                PHPUnit_Runner_Version::getReleaseChannel()
            );
        }

        $tempFilename = tempnam(sys_get_temp_dir(), 'phpunit') . '.phar';

        // Workaround for https://bugs.php.net/bug.php?id=65538
        $caFile = dirname($tempFilename) . '/ca.pem';
        copy(__PHPUNIT_PHAR_ROOT__ . '/ca.pem', $caFile);

        print 'Updating the PHPUnit PHAR ... ';

        $options = [
            'ssl' => [
                'allow_self_signed' => false,
                'cafile'            => $caFile,
                'verify_peer'       => true
            ]
        ];

        file_put_contents(
            $tempFilename,
            file_get_contents(
                $remoteFilename,
                false,
                stream_context_create($options)
            )
        );

        chmod($tempFilename, 0777 & ~umask());

        try {
            $phar = new Phar($tempFilename);
            unset($phar);
            rename($tempFilename, $localFilename);
            unlink($caFile);
        } catch (Throwable $_e) {
            $e = $_e;
        } catch (Exception $_e) {
            $e = $_e;
        }

        if (isset($e)) {
            unlink($caFile);
            unlink($tempFilename);
            print " done\n\n" . $e->getMessage() . "\n";
            exit(2);
        }

        print " done\n";
        exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
    }

    protected function handleVersionCheck()
    {
        $this->printVersionString();

        $latestVersion = file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit');
        $isOutdated    = version_compare($latestVersion, PHPUnit_Runner_Version::id(), '>');

        if ($isOutdated) {
            print "You are not using the latest version of PHPUnit.\n";
            print 'Use "phpunit --self-upgrade" to install PHPUnit ' . $latestVersion . "\n";
        } else {
            print "You are using the latest version of PHPUnit.\n";
        }

        exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
    }

    /**
     * Show the help message.
     */
    protected function showHelp()
    {
        $this->printVersionString();

        print <<<EOT
Usage: phpunit [options] UnitTest [UnitTest.php]
       phpunit [options] <directory>

Code Coverage Options:

  --coverage-clover <file>  Generate code coverage report in Clover XML format.
  --coverage-crap4j <file>  Generate code coverage report in Crap4J XML format.
  --coverage-html <dir>     Generate code coverage report in HTML format.
  --coverage-php <file>     Export PHP_CodeCoverage object to file.
  --coverage-text=<file>    Generate code coverage report in text format.
                            Default: Standard output.
  --coverage-xml <dir>      Generate code coverage report in PHPUnit XML format.
  --whitelist <dir>         Whitelist <dir> for code coverage analysis.
  --disable-coverage-ignore Disable annotations for ignoring code coverage.

Logging Options:

  --log-junit <file>        Log test execution in JUnit XML format to file.
  --log-teamcity <file>     Log test execution in TeamCity format to file.
  --testdox-html <file>     Write agile documentation in HTML format to file.
  --testdox-text <file>     Write agile documentation in Text format to file.
  --testdox-xml <file>      Write agile documentation in XML format to file.
  --reverse-list            Print defects in reverse order

Test Selection Options:

  --filter <pattern>        Filter which tests to run.
  --testsuite <name>        Filter which testsuite to run.
  --group ...               Only runs tests from the specified group(s).
  --exclude-group ...       Exclude tests from the specified group(s).
  --list-groups             List available test groups.
  --list-suites             List available test suites.
  --test-suffix ...         Only search for test in files with specified
                            suffix(es). Default: Test.php,.phpt

Test Execution Options:

  --report-useless-tests    Be strict about tests that do not test anything.
  --strict-coverage         Be strict about @covers annotation usage.
  --strict-global-state     Be strict about changes to global state
  --disallow-test-output    Be strict about output during tests.
  --disallow-resource-usage Be strict about resource usage during small tests.
  --enforce-time-limit      Enforce time limit based on test size.
  --disallow-todo-tests     Disallow @todo-annotated tests.

  --process-isolation       Run each test in a separate PHP process.
  --no-globals-backup       Do not backup and restore \$GLOBALS for each test.
  --static-backup           Backup and restore static attributes for each test.

  --colors=<flag>           Use colors in output ("never", "auto" or "always").
  --columns <n>             Number of columns to use for progress output.
  --columns max             Use maximum number of columns for progress output.
  --stderr                  Write to STDERR instead of STDOUT.
  --stop-on-error           Stop execution upon first error.
  --stop-on-failure         Stop execution upon first error or failure.
  --stop-on-warning         Stop execution upon first warning.
  --stop-on-risky           Stop execution upon first risky test.
  --stop-on-skipped         Stop execution upon first skipped test.
  --stop-on-incomplete      Stop execution upon first incomplete test.
  --fail-on-warning         Treat tests with warnings as failures.
  --fail-on-risky           Treat risky tests as failures.
  -v|--verbose              Output more verbose information.
  --debug                   Display debugging information during test execution.

  --loader <loader>         TestSuiteLoader implementation to use.
  --repeat <times>          Runs the test(s) repeatedly.
  --teamcity                Report test execution progress in TeamCity format.
  --testdox                 Report test execution progress in TestDox format.
  --testdox-group           Only include tests from the specified group(s).
  --testdox-exclude-group   Exclude tests from the specified group(s).
  --printer <printer>       TestListener implementation to use.

Configuration Options:

  --bootstrap <file>        A "bootstrap" PHP file that is run before the tests.
  -c|--configuration <file> Read configuration from XML file.
  --no-configuration        Ignore default configuration file (phpunit.xml).
  --no-coverage             Ignore code coverage configuration.
  --no-extensions           Do not load PHPUnit extensions.
  --include-path <path(s)>  Prepend PHP's include_path with given path(s).
  -d key[=value]            Sets a php.ini value.
  --generate-configuration  Generate configuration file with suggested settings.

Miscellaneous Options:

  -h|--help                 Prints this usage information.
  --version                 Prints the version and exits.
  --atleast-version <min>   Checks that version is greater than min and exits.

EOT;

        if (defined('__PHPUNIT_PHAR__')) {
            print "\n  --check-version           Check whether PHPUnit is the latest version.";
        }
    }

    /**
     * Custom callback for test suite discovery.
     */
    protected function handleCustomTestSuite()
    {
    }

    private function printVersionString()
    {
        if ($this->versionStringPrinted) {
            return;
        }

        print PHPUnit_Runner_Version::getVersionString() . "\n\n";

        $this->versionStringPrinted = true;
    }

    /**
     * @param string $message
     */
    private function showError($message)
    {
        $this->printVersionString();

        print $message . "\n";

        exit(PHPUnit_TextUI_TestRunner::FAILURE_EXIT);
    }

    /**
     * @param string $directory
     */
    private function handleExtensions($directory)
    {
        $facade = new File_Iterator_Facade;

        foreach ($facade->getFilesAsArray($directory, '.phar') as $file) {
            require $file;

            $this->arguments['loadedExtensions'][] = $file;
        }
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use SebastianBergmann\Environment\Console;

/**
 * Prints the result of a TextUI TestRunner run.
 */
class PHPUnit_TextUI_ResultPrinter extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
{
    const EVENT_TEST_START      = 0;
    const EVENT_TEST_END        = 1;
    const EVENT_TESTSUITE_START = 2;
    const EVENT_TESTSUITE_END   = 3;

    const COLOR_NEVER   = 'never';
    const COLOR_AUTO    = 'auto';
    const COLOR_ALWAYS  = 'always';
    const COLOR_DEFAULT = self::COLOR_NEVER;

    /**
     * @var array
     */
    private static $ansiCodes = [
      'bold'       => 1,
      'fg-black'   => 30,
      'fg-red'     => 31,
      'fg-green'   => 32,
      'fg-yellow'  => 33,
      'fg-blue'    => 34,
      'fg-magenta' => 35,
      'fg-cyan'    => 36,
      'fg-white'   => 37,
      'bg-black'   => 40,
      'bg-red'     => 41,
      'bg-green'   => 42,
      'bg-yellow'  => 43,
      'bg-blue'    => 44,
      'bg-magenta' => 45,
      'bg-cyan'    => 46,
      'bg-white'   => 47
    ];

    /**
     * @var int
     */
    protected $column = 0;

    /**
     * @var int
     */
    protected $maxColumn;

    /**
     * @var bool
     */
    protected $lastTestFailed = false;

    /**
     * @var int
     */
    protected $numAssertions = 0;

    /**
     * @var int
     */
    protected $numTests = -1;

    /**
     * @var int
     */
    protected $numTestsRun = 0;

    /**
     * @var int
     */
    protected $numTestsWidth;

    /**
     * @var bool
     */
    protected $colors = false;

    /**
     * @var bool
     */
    protected $debug = false;

    /**
     * @var bool
     */
    protected $verbose = false;

    /**
     * @var int
     */
    private $numberOfColumns;

    /**
     * @var bool
     */
    private $reverse = false;

    /**
     * @var bool
     */
    private $defectListPrinted = false;

    /**
     * Constructor.
     *
     * @param mixed      $out
     * @param bool       $verbose
     * @param string     $colors
     * @param bool       $debug
     * @param int|string $numberOfColumns
     * @param bool       $reverse
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function __construct($out = null, $verbose = false, $colors = self::COLOR_DEFAULT, $debug = false, $numberOfColumns = 80, $reverse = false)
    {
        parent::__construct($out);

        if (!is_bool($verbose)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'boolean');
        }

        $availableColors = [self::COLOR_NEVER, self::COLOR_AUTO, self::COLOR_ALWAYS];

        if (!in_array($colors, $availableColors)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(
                3,
                vsprintf('value from "%s", "%s" or "%s"', $availableColors)
            );
        }

        if (!is_bool($debug)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(4, 'boolean');
        }

        if (!is_int($numberOfColumns) && $numberOfColumns != 'max') {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(5, 'integer or "max"');
        }

        if (!is_bool($reverse)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(6, 'boolean');
        }

        $console            = new Console;
        $maxNumberOfColumns = $console->getNumberOfColumns();

        if ($numberOfColumns == 'max' || $numberOfColumns > $maxNumberOfColumns) {
            $numberOfColumns = $maxNumberOfColumns;
        }

        $this->numberOfColumns = $numberOfColumns;
        $this->verbose         = $verbose;
        $this->debug           = $debug;
        $this->reverse         = $reverse;

        if ($colors === self::COLOR_AUTO && $console->hasColorSupport()) {
            $this->colors = true;
        } else {
            $this->colors = (self::COLOR_ALWAYS === $colors);
        }
    }

    /**
     * @param PHPUnit_Framework_TestResult $result
     */
    public function printResult(PHPUnit_Framework_TestResult $result)
    {
        $this->printHeader();
        $this->printErrors($result);
        $this->printWarnings($result);
        $this->printFailures($result);

        if ($this->verbose) {
            $this->printRisky($result);
            $this->printIncompletes($result);
            $this->printSkipped($result);
        }

        $this->printFooter($result);
    }

    /**
     * @param array  $defects
     * @param string $type
     */
    protected function printDefects(array $defects, $type)
    {
        $count = count($defects);

        if ($count == 0) {
            return;
        }

        if ($this->defectListPrinted) {
            $this->write("\n--\n\n");
        }

        $this->write(
            sprintf(
                "There %s %d %s%s:\n",
                ($count == 1) ? 'was' : 'were',
                $count,
                $type,
                ($count == 1) ? '' : 's'
            )
        );

        $i = 1;

        if ($this->reverse) {
            $defects = array_reverse($defects);
        }

        foreach ($defects as $defect) {
            $this->printDefect($defect, $i++);
        }

        $this->defectListPrinted = true;
    }

    /**
     * @param PHPUnit_Framework_TestFailure $defect
     * @param int                           $count
     */
    protected function printDefect(PHPUnit_Framework_TestFailure $defect, $count)
    {
        $this->printDefectHeader($defect, $count);
        $this->printDefectTrace($defect);
    }

    /**
     * @param PHPUnit_Framework_TestFailure $defect
     * @param int                           $count
     */
    protected function printDefectHeader(PHPUnit_Framework_TestFailure $defect, $count)
    {
        $this->write(
            sprintf(
                "\n%d) %s\n",
                $count,
                $defect->getTestName()
            )
        );
    }

    /**
     * @param PHPUnit_Framework_TestFailure $defect
     */
    protected function printDefectTrace(PHPUnit_Framework_TestFailure $defect)
    {
        $e = $defect->thrownException();
        $this->write((string) $e);

        while ($e = $e->getPrevious()) {
            $this->write("\nCaused by\n" . $e);
        }
    }

    /**
     * @param PHPUnit_Framework_TestResult $result
     */
    protected function printErrors(PHPUnit_Framework_TestResult $result)
    {
        $this->printDefects($result->errors(), 'error');
    }

    /**
     * @param PHPUnit_Framework_TestResult $result
     */
    protected function printFailures(PHPUnit_Framework_TestResult $result)
    {
        $this->printDefects($result->failures(), 'failure');
    }

    /**
     * @param PHPUnit_Framework_TestResult $result
     */
    protected function printWarnings(PHPUnit_Framework_TestResult $result)
    {
        $this->printDefects($result->warnings(), 'warning');
    }

    /**
     * @param PHPUnit_Framework_TestResult $result
     */
    protected function printIncompletes(PHPUnit_Framework_TestResult $result)
    {
        $this->printDefects($result->notImplemented(), 'incomplete test');
    }

    /**
     * @param PHPUnit_Framework_TestResult $result
     */
    protected function printRisky(PHPUnit_Framework_TestResult $result)
    {
        $this->printDefects($result->risky(), 'risky test');
    }

    /**
     * @param PHPUnit_Framework_TestResult $result
     */
    protected function printSkipped(PHPUnit_Framework_TestResult $result)
    {
        $this->printDefects($result->skipped(), 'skipped test');
    }

    protected function printHeader()
    {
        $this->write("\n\n" . PHP_Timer::resourceUsage() . "\n\n");
    }

    /**
     * @param PHPUnit_Framework_TestResult $result
     */
    protected function printFooter(PHPUnit_Framework_TestResult $result)
    {
        if (count($result) === 0) {
            $this->writeWithColor(
                'fg-black, bg-yellow',
                'No tests executed!'
            );

            return;
        }

        if ($result->wasSuccessful() &&
            $result->allHarmless() &&
            $result->allCompletelyImplemented() &&
            $result->noneSkipped()) {
            $this->writeWithColor(
                'fg-black, bg-green',
                sprintf(
                    'OK (%d test%s, %d assertion%s)',
                    count($result),
                    (count($result) == 1) ? '' : 's',
                    $this->numAssertions,
                    ($this->numAssertions == 1) ? '' : 's'
                )
            );
        } else {
            if ($result->wasSuccessful()) {
                $color = 'fg-black, bg-yellow';

                if ($this->verbose) {
                    $this->write("\n");
                }

                $this->writeWithColor(
                    $color,
                    'OK, but incomplete, skipped, or risky tests!'
                );
            } else {
                $this->write("\n");

                if ($result->errorCount()) {
                    $color = 'fg-white, bg-red';

                    $this->writeWithColor(
                        $color,
                        'ERRORS!'
                    );
                } elseif ($result->failureCount()) {
                    $color = 'fg-white, bg-red';

                    $this->writeWithColor(
                        $color,
                        'FAILURES!'
                    );
                } elseif ($result->warningCount()) {
                    $color = 'fg-black, bg-yellow';

                    $this->writeWithColor(
                        $color,
                        'WARNINGS!'
                    );
                }
            }

            $this->writeCountString(count($result), 'Tests', $color, true);
            $this->writeCountString($this->numAssertions, 'Assertions', $color, true);
            $this->writeCountString($result->errorCount(), 'Errors', $color);
            $this->writeCountString($result->failureCount(), 'Failures', $color);
            $this->writeCountString($result->warningCount(), 'Warnings', $color);
            $this->writeCountString($result->skippedCount(), 'Skipped', $color);
            $this->writeCountString($result->notImplementedCount(), 'Incomplete', $color);
            $this->writeCountString($result->riskyCount(), 'Risky', $color);
            $this->writeWithColor($color, '.', true);
        }
    }

    public function printWaitPrompt()
    {
        $this->write("\n<RETURN> to continue\n");
    }

    /**
     * An error occurred.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->writeProgressWithColor('fg-red, bold', 'E');
        $this->lastTestFailed = true;
    }

    /**
     * A failure occurred.
     *
     * @param PHPUnit_Framework_Test                 $test
     * @param PHPUnit_Framework_AssertionFailedError $e
     * @param float                                  $time
     */
    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
    {
        $this->writeProgressWithColor('bg-red, fg-white', 'F');
        $this->lastTestFailed = true;
    }

    /**
     * A warning occurred.
     *
     * @param PHPUnit_Framework_Test    $test
     * @param PHPUnit_Framework_Warning $e
     * @param float                     $time
     */
    public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
    {
        $this->writeProgressWithColor('fg-yellow, bold', 'W');
        $this->lastTestFailed = true;
    }

    /**
     * Incomplete test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->writeProgressWithColor('fg-yellow, bold', 'I');
        $this->lastTestFailed = true;
    }

    /**
     * Risky test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->writeProgressWithColor('fg-yellow, bold', 'R');
        $this->lastTestFailed = true;
    }

    /**
     * Skipped test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->writeProgressWithColor('fg-cyan, bold', 'S');
        $this->lastTestFailed = true;
    }

    /**
     * A testsuite started.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        if ($this->numTests == -1) {
            $this->numTests      = count($suite);
            $this->numTestsWidth = strlen((string) $this->numTests);
            $this->maxColumn     = $this->numberOfColumns - strlen('  /  (XXX%)') - (2 * $this->numTestsWidth);
        }
    }

    /**
     * A testsuite ended.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
    }

    /**
     * A test started.
     *
     * @param PHPUnit_Framework_Test $test
     */
    public function startTest(PHPUnit_Framework_Test $test)
    {
        if ($this->debug) {
            $this->write(
                sprintf(
                    "\nStarting test '%s'.\n",
                    PHPUnit_Util_Test::describe($test)
                )
            );
        }
    }

    /**
     * A test ended.
     *
     * @param PHPUnit_Framework_Test $test
     * @param float                  $time
     */
    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
        if (!$this->lastTestFailed) {
            $this->writeProgress('.');
        }

        if ($test instanceof PHPUnit_Framework_TestCase) {
            $this->numAssertions += $test->getNumAssertions();
        } elseif ($test instanceof PHPUnit_Extensions_PhptTestCase) {
            $this->numAssertions++;
        }

        $this->lastTestFailed = false;

        if ($test instanceof PHPUnit_Framework_TestCase) {
            if (!$test->hasExpectationOnOutput()) {
                $this->write($test->getActualOutput());
            }
        }
    }

    /**
     * @param string $progress
     */
    protected function writeProgress($progress)
    {
        $this->write($progress);
        $this->column++;
        $this->numTestsRun++;

        if ($this->column == $this->maxColumn
            || $this->numTestsRun == $this->numTests
        ) {
            if ($this->numTestsRun == $this->numTests) {
                $this->write(str_repeat(' ', $this->maxColumn - $this->column));
            }

            $this->write(
                sprintf(
                    ' %' . $this->numTestsWidth . 'd / %' .
                    $this->numTestsWidth . 'd (%3s%%)',
                    $this->numTestsRun,
                    $this->numTests,
                    floor(($this->numTestsRun / $this->numTests) * 100)
                )
            );

            if ($this->column == $this->maxColumn) {
                $this->writeNewLine();
            }
        }
    }

    protected function writeNewLine()
    {
        $this->column = 0;
        $this->write("\n");
    }

    /**
     * Formats a buffer with a specified ANSI color sequence if colors are
     * enabled.
     *
     * @param string $color
     * @param string $buffer
     *
     * @return string
     */
    protected function formatWithColor($color, $buffer)
    {
        if (!$this->colors) {
            return $buffer;
        }

        $codes   = array_map('trim', explode(',', $color));
        $lines   = explode("\n", $buffer);
        $padding = max(array_map('strlen', $lines));
        $styles  = [];

        foreach ($codes as $code) {
            $styles[] = self::$ansiCodes[$code];
        }

        $style = sprintf("\x1b[%sm", implode(';', $styles));

        $styledLines = [];

        foreach ($lines as $line) {
            $styledLines[] = $style . str_pad($line, $padding) . "\x1b[0m";
        }

        return implode("\n", $styledLines);
    }

    /**
     * Writes a buffer out with a color sequence if colors are enabled.
     *
     * @param string $color
     * @param string $buffer
     * @param bool   $lf
     */
    protected function writeWithColor($color, $buffer, $lf = true)
    {
        $this->write($this->formatWithColor($color, $buffer));

        if ($lf) {
            $this->write("\n");
        }
    }

    /**
     * Writes progress with a color sequence if colors are enabled.
     *
     * @param string $color
     * @param string $buffer
     */
    protected function writeProgressWithColor($color, $buffer)
    {
        $buffer = $this->formatWithColor($color, $buffer);
        $this->writeProgress($buffer);
    }

    /**
     * @param int    $count
     * @param string $name
     * @param string $color
     * @param bool   $always
     */
    private function writeCountString($count, $name, $color, $always = false)
    {
        static $first = true;

        if ($always || $count > 0) {
            $this->writeWithColor(
                $color,
                sprintf(
                    '%s%s: %d',
                    !$first ? ', ' : '',
                    $name,
                    $count
                ),
                false
            );

            $first = false;
        }
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Exception as CodeCoverageException;
use SebastianBergmann\CodeCoverage\Filter as CodeCoverageFilter;
use SebastianBergmann\CodeCoverage\Report\Clover as CloverReport;
use SebastianBergmann\CodeCoverage\Report\Crap4j as Crap4jReport;
use SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlReport;
use SebastianBergmann\CodeCoverage\Report\PHP as PhpReport;
use SebastianBergmann\CodeCoverage\Report\Text as TextReport;
use SebastianBergmann\CodeCoverage\Report\Xml\Facade as XmlReport;
use SebastianBergmann\Environment\Runtime;

/**
 * A TestRunner for the Command Line Interface (CLI)
 * PHP SAPI Module.
 */
class PHPUnit_TextUI_TestRunner extends PHPUnit_Runner_BaseTestRunner
{
    const SUCCESS_EXIT   = 0;
    const FAILURE_EXIT   = 1;
    const EXCEPTION_EXIT = 2;

    /**
     * @var CodeCoverageFilter
     */
    protected $codeCoverageFilter;

    /**
     * @var PHPUnit_Runner_TestSuiteLoader
     */
    protected $loader = null;

    /**
     * @var PHPUnit_TextUI_ResultPrinter
     */
    protected $printer = null;

    /**
     * @var bool
     */
    protected static $versionStringPrinted = false;

    /**
     * @var Runtime
     */
    private $runtime;

    /**
     * @var bool
     */
    private $messagePrinted = false;

    /**
     * @param PHPUnit_Runner_TestSuiteLoader $loader
     * @param CodeCoverageFilter             $filter
     */
    public function __construct(PHPUnit_Runner_TestSuiteLoader $loader = null, CodeCoverageFilter $filter = null)
    {
        if ($filter === null) {
            $filter = new CodeCoverageFilter;
        }

        $this->codeCoverageFilter = $filter;
        $this->loader             = $loader;
        $this->runtime            = new Runtime;
    }

    /**
     * @param PHPUnit_Framework_Test|ReflectionClass $test
     * @param array                                  $arguments
     *
     * @return PHPUnit_Framework_TestResult
     *
     * @throws PHPUnit_Framework_Exception
     */
    public static function run($test, array $arguments = [])
    {
        if ($test instanceof ReflectionClass) {
            $test = new PHPUnit_Framework_TestSuite($test);
        }

        if ($test instanceof PHPUnit_Framework_Test) {
            $aTestRunner = new self;

            return $aTestRunner->doRun(
                $test,
                $arguments
            );
        } else {
            throw new PHPUnit_Framework_Exception(
                'No test case or test suite found.'
            );
        }
    }

    /**
     * @return PHPUnit_Framework_TestResult
     */
    protected function createTestResult()
    {
        return new PHPUnit_Framework_TestResult;
    }

    /**
     * @param PHPUnit_Framework_TestSuite $suite
     * @param array                       $arguments
     */
    private function processSuiteFilters(PHPUnit_Framework_TestSuite $suite, array $arguments)
    {
        if (!$arguments['filter'] &&
            empty($arguments['groups']) &&
            empty($arguments['excludeGroups'])) {
            return;
        }

        $filterFactory = new PHPUnit_Runner_Filter_Factory();

        if (!empty($arguments['excludeGroups'])) {
            $filterFactory->addFilter(
                new ReflectionClass('PHPUnit_Runner_Filter_Group_Exclude'),
                $arguments['excludeGroups']
            );
        }

        if (!empty($arguments['groups'])) {
            $filterFactory->addFilter(
                new ReflectionClass('PHPUnit_Runner_Filter_Group_Include'),
                $arguments['groups']
            );
        }

        if ($arguments['filter']) {
            $filterFactory->addFilter(
                new ReflectionClass('PHPUnit_Runner_Filter_Test'),
                $arguments['filter']
            );
        }
        $suite->injectFilter($filterFactory);
    }

    /**
     * @param PHPUnit_Framework_Test $suite
     * @param array                  $arguments
     * @param bool                   $exit
     *
     * @return PHPUnit_Framework_TestResult
     */
    public function doRun(PHPUnit_Framework_Test $suite, array $arguments = [], $exit = true)
    {
        if (isset($arguments['configuration'])) {
            $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] = $arguments['configuration'];
        }

        $this->handleConfiguration($arguments);

        $this->processSuiteFilters($suite, $arguments);

        if (isset($arguments['bootstrap'])) {
            $GLOBALS['__PHPUNIT_BOOTSTRAP'] = $arguments['bootstrap'];
        }

        if ($arguments['backupGlobals'] === false) {
            $suite->setBackupGlobals(false);
        }

        if ($arguments['backupStaticAttributes'] === true) {
            $suite->setBackupStaticAttributes(true);
        }

        if ($arguments['beStrictAboutChangesToGlobalState'] === true) {
            $suite->setbeStrictAboutChangesToGlobalState(true);
        }

        if (is_int($arguments['repeat'])) {
            $test = new PHPUnit_Extensions_RepeatedTest(
                $suite,
                $arguments['repeat'],
                $arguments['processIsolation']
            );

            $suite = new PHPUnit_Framework_TestSuite();
            $suite->addTest($test);
        }

        $result = $this->createTestResult();

        if (!$arguments['convertErrorsToExceptions']) {
            $result->convertErrorsToExceptions(false);
        }

        if (!$arguments['convertNoticesToExceptions']) {
            PHPUnit_Framework_Error_Notice::$enabled = false;
        }

        if (!$arguments['convertWarningsToExceptions']) {
            PHPUnit_Framework_Error_Warning::$enabled = false;
        }

        if ($arguments['stopOnError']) {
            $result->stopOnError(true);
        }

        if ($arguments['stopOnFailure']) {
            $result->stopOnFailure(true);
        }

        if ($arguments['stopOnWarning']) {
            $result->stopOnWarning(true);
        }

        if ($arguments['stopOnIncomplete']) {
            $result->stopOnIncomplete(true);
        }

        if ($arguments['stopOnRisky']) {
            $result->stopOnRisky(true);
        }

        if ($arguments['stopOnSkipped']) {
            $result->stopOnSkipped(true);
        }

        if ($arguments['registerMockObjectsFromTestArgumentsRecursively']) {
            $result->setRegisterMockObjectsFromTestArgumentsRecursively(true);
        }

        if ($this->printer === null) {
            if (isset($arguments['printer']) &&
                $arguments['printer'] instanceof PHPUnit_Util_Printer) {
                $this->printer = $arguments['printer'];
            } else {
                $printerClass = 'PHPUnit_TextUI_ResultPrinter';

                if (isset($arguments['printer']) &&
                    is_string($arguments['printer']) &&
                    class_exists($arguments['printer'], false)) {
                    $class = new ReflectionClass($arguments['printer']);

                    if ($class->isSubclassOf('PHPUnit_TextUI_ResultPrinter')) {
                        $printerClass = $arguments['printer'];
                    }
                }

                $this->printer = new $printerClass(
                    (isset($arguments['stderr']) && $arguments['stderr'] === true) ? 'php://stderr' : null,
                    $arguments['verbose'],
                    $arguments['colors'],
                    $arguments['debug'],
                    $arguments['columns'],
                    $arguments['reverseList']
                );
            }
        }

        if (!$this->printer instanceof PHPUnit_Util_Log_TAP) {
            $this->printer->write(
                PHPUnit_Runner_Version::getVersionString() . "\n"
            );

            self::$versionStringPrinted = true;

            if ($arguments['verbose']) {
                $runtime = $this->runtime->getNameWithVersion();

                if ($this->runtime->hasXdebug()) {
                    $runtime .= sprintf(
                        ' with Xdebug %s',
                        phpversion('xdebug')
                    );
                }

                $this->writeMessage('Runtime', $runtime);

                if (isset($arguments['configuration'])) {
                    $this->writeMessage(
                        'Configuration',
                        $arguments['configuration']->getFilename()
                    );
                }

                foreach ($arguments['loadedExtensions'] as $extension) {
                    $this->writeMessage(
                        'Extension',
                        $extension
                    );
                }

                foreach ($arguments['notLoadedExtensions'] as $extension) {
                    $this->writeMessage(
                        'Extension',
                        $extension
                    );
                }
            }

            if (isset($arguments['deprecatedCheckForUnintentionallyCoveredCodeSettingUsed'])) {
                $this->writeMessage('Warning', 'Deprecated configuration setting "checkForUnintentionallyCoveredCode" used');
            }

            if (isset($arguments['tapLogfile'])) {
                $this->writeMessage('Warning', 'Deprecated TAP test listener used');
            }

            if (isset($arguments['jsonLogfile'])) {
                $this->writeMessage('Warning', 'Deprecated JSON test listener used');
            }
        }

        foreach ($arguments['listeners'] as $listener) {
            $result->addListener($listener);
        }

        $result->addListener($this->printer);

        if (isset($arguments['testdoxHTMLFile'])) {
            $result->addListener(
                new PHPUnit_Util_TestDox_ResultPrinter_HTML(
                    $arguments['testdoxHTMLFile'],
                    $arguments['testdoxGroups'],
                    $arguments['testdoxExcludeGroups']
                )
            );
        }

        if (isset($arguments['testdoxTextFile'])) {
            $result->addListener(
                new PHPUnit_Util_TestDox_ResultPrinter_Text(
                    $arguments['testdoxTextFile'],
                    $arguments['testdoxGroups'],
                    $arguments['testdoxExcludeGroups']
                )
            );
        }

        if (isset($arguments['testdoxXMLFile'])) {
            $result->addListener(
                new PHPUnit_Util_TestDox_ResultPrinter_XML(
                    $arguments['testdoxXMLFile']
                )
            );
        }

        $codeCoverageReports = 0;

        if (isset($arguments['coverageClover'])) {
            $codeCoverageReports++;
        }

        if (isset($arguments['coverageCrap4J'])) {
            $codeCoverageReports++;
        }

        if (isset($arguments['coverageHtml'])) {
            $codeCoverageReports++;
        }

        if (isset($arguments['coveragePHP'])) {
            $codeCoverageReports++;
        }

        if (isset($arguments['coverageText'])) {
            $codeCoverageReports++;
        }

        if (isset($arguments['coverageXml'])) {
            $codeCoverageReports++;
        }

        if (isset($arguments['noCoverage'])) {
            $codeCoverageReports = 0;
        }

        if ($codeCoverageReports > 0 && !$this->runtime->canCollectCodeCoverage()) {
            $this->writeMessage('Error', 'No code coverage driver is available');

            $codeCoverageReports = 0;
        }

        if (!$this->printer instanceof PHPUnit_Util_Log_TAP) {
            $this->printer->write("\n");
        }

        if ($codeCoverageReports > 0) {
            $codeCoverage = new CodeCoverage(
                null,
                $this->codeCoverageFilter
            );

            $codeCoverage->setUnintentionallyCoveredSubclassesWhitelist(
                [SebastianBergmann\Comparator\Comparator::class]
            );

            $codeCoverage->setCheckForUnintentionallyCoveredCode(
                $arguments['strictCoverage']
            );

            $codeCoverage->setCheckForMissingCoversAnnotation(
                $arguments['strictCoverage']
            );

            if (isset($arguments['forceCoversAnnotation'])) {
                $codeCoverage->setForceCoversAnnotation(
                    $arguments['forceCoversAnnotation']
                );
            }

            if (isset($arguments['disableCodeCoverageIgnore'])) {
                $codeCoverage->setDisableIgnoredLines(true);
            }

            if (isset($arguments['whitelist'])) {
                $this->codeCoverageFilter->addDirectoryToWhitelist($arguments['whitelist']);
            }

            if (isset($arguments['configuration'])) {
                $filterConfiguration = $arguments['configuration']->getFilterConfiguration();

                $codeCoverage->setAddUncoveredFilesFromWhitelist(
                    $filterConfiguration['whitelist']['addUncoveredFilesFromWhitelist']
                );

                $codeCoverage->setProcessUncoveredFilesFromWhitelist(
                    $filterConfiguration['whitelist']['processUncoveredFilesFromWhitelist']
                );

                foreach ($filterConfiguration['whitelist']['include']['directory'] as $dir) {
                    $this->codeCoverageFilter->addDirectoryToWhitelist(
                        $dir['path'],
                        $dir['suffix'],
                        $dir['prefix']
                    );
                }

                foreach ($filterConfiguration['whitelist']['include']['file'] as $file) {
                    $this->codeCoverageFilter->addFileToWhitelist($file);
                }

                foreach ($filterConfiguration['whitelist']['exclude']['directory'] as $dir) {
                    $this->codeCoverageFilter->removeDirectoryFromWhitelist(
                        $dir['path'],
                        $dir['suffix'],
                        $dir['prefix']
                    );
                }

                foreach ($filterConfiguration['whitelist']['exclude']['file'] as $file) {
                    $this->codeCoverageFilter->removeFileFromWhitelist($file);
                }
            }

            if (!$this->codeCoverageFilter->hasWhitelist()) {
                $this->writeMessage('Error', 'No whitelist configured, no code coverage will be generated');

                $codeCoverageReports = 0;

                unset($codeCoverage);
            }
        }

        if (isset($codeCoverage)) {
            $result->setCodeCoverage($codeCoverage);

            if ($codeCoverageReports > 1 && isset($arguments['cacheTokens'])) {
                $codeCoverage->setCacheTokens($arguments['cacheTokens']);
            }
        }

        if (isset($arguments['jsonLogfile'])) {
            $result->addListener(
                new PHPUnit_Util_Log_JSON($arguments['jsonLogfile'])
            );
        }

        if (isset($arguments['tapLogfile'])) {
            $result->addListener(
                new PHPUnit_Util_Log_TAP($arguments['tapLogfile'])
            );
        }

        if (isset($arguments['teamcityLogfile'])) {
            $result->addListener(
                new PHPUnit_Util_Log_TeamCity($arguments['teamcityLogfile'])
            );
        }

        if (isset($arguments['junitLogfile'])) {
            $result->addListener(
                new PHPUnit_Util_Log_JUnit(
                    $arguments['junitLogfile'],
                    $arguments['logIncompleteSkipped']
                )
            );
        }

        $result->beStrictAboutTestsThatDoNotTestAnything($arguments['reportUselessTests']);
        $result->beStrictAboutOutputDuringTests($arguments['disallowTestOutput']);
        $result->beStrictAboutTodoAnnotatedTests($arguments['disallowTodoAnnotatedTests']);
        $result->beStrictAboutResourceUsageDuringSmallTests($arguments['beStrictAboutResourceUsageDuringSmallTests']);
        $result->enforceTimeLimit($arguments['enforceTimeLimit']);
        $result->setTimeoutForSmallTests($arguments['timeoutForSmallTests']);
        $result->setTimeoutForMediumTests($arguments['timeoutForMediumTests']);
        $result->setTimeoutForLargeTests($arguments['timeoutForLargeTests']);

        if ($suite instanceof PHPUnit_Framework_TestSuite) {
            $suite->setRunTestInSeparateProcess($arguments['processIsolation']);
        }

        $suite->run($result);

        unset($suite);
        $result->flushListeners();

        if ($this->printer instanceof PHPUnit_TextUI_ResultPrinter) {
            $this->printer->printResult($result);
        }

        if (isset($codeCoverage)) {
            if (isset($arguments['coverageClover'])) {
                $this->printer->write(
                    "\nGenerating code coverage report in Clover XML format ..."
                );

                try {
                    $writer = new CloverReport();
                    $writer->process($codeCoverage, $arguments['coverageClover']);

                    $this->printer->write(" done\n");
                    unset($writer);
                } catch (CodeCoverageException $e) {
                    $this->printer->write(
                        " failed\n" . $e->getMessage() . "\n"
                    );
                }
            }

            if (isset($arguments['coverageCrap4J'])) {
                $this->printer->write(
                    "\nGenerating Crap4J report XML file ..."
                );

                try {
                    $writer = new Crap4jReport($arguments['crap4jThreshold']);
                    $writer->process($codeCoverage, $arguments['coverageCrap4J']);

                    $this->printer->write(" done\n");
                    unset($writer);
                } catch (CodeCoverageException $e) {
                    $this->printer->write(
                        " failed\n" . $e->getMessage() . "\n"
                    );
                }
            }

            if (isset($arguments['coverageHtml'])) {
                $this->printer->write(
                    "\nGenerating code coverage report in HTML format ..."
                );

                try {
                    $writer = new HtmlReport(
                        $arguments['reportLowUpperBound'],
                        $arguments['reportHighLowerBound'],
                        sprintf(
                            ' and <a href="https://phpunit.de/">PHPUnit %s</a>',
                            PHPUnit_Runner_Version::id()
                        )
                    );

                    $writer->process($codeCoverage, $arguments['coverageHtml']);

                    $this->printer->write(" done\n");
                    unset($writer);
                } catch (CodeCoverageException $e) {
                    $this->printer->write(
                        " failed\n" . $e->getMessage() . "\n"
                    );
                }
            }

            if (isset($arguments['coveragePHP'])) {
                $this->printer->write(
                    "\nGenerating code coverage report in PHP format ..."
                );

                try {
                    $writer = new PhpReport();
                    $writer->process($codeCoverage, $arguments['coveragePHP']);

                    $this->printer->write(" done\n");
                    unset($writer);
                } catch (CodeCoverageException $e) {
                    $this->printer->write(
                        " failed\n" . $e->getMessage() . "\n"
                    );
                }
            }

            if (isset($arguments['coverageText'])) {
                if ($arguments['coverageText'] == 'php://stdout') {
                    $outputStream = $this->printer;
                    $colors       = $arguments['colors'] && $arguments['colors'] != PHPUnit_TextUI_ResultPrinter::COLOR_NEVER;
                } else {
                    $outputStream = new PHPUnit_Util_Printer($arguments['coverageText']);
                    $colors       = false;
                }

                $processor = new TextReport(
                    $arguments['reportLowUpperBound'],
                    $arguments['reportHighLowerBound'],
                    $arguments['coverageTextShowUncoveredFiles'],
                    $arguments['coverageTextShowOnlySummary']
                );

                $outputStream->write(
                    $processor->process($codeCoverage, $colors)
                );
            }

            if (isset($arguments['coverageXml'])) {
                $this->printer->write(
                    "\nGenerating code coverage report in PHPUnit XML format ..."
                );

                try {
                    $writer = new XmlReport;
                    $writer->process($codeCoverage, $arguments['coverageXml']);

                    $this->printer->write(" done\n");
                    unset($writer);
                } catch (CodeCoverageException $e) {
                    $this->printer->write(
                        " failed\n" . $e->getMessage() . "\n"
                    );
                }
            }
        }

        if ($exit) {
            if ($result->wasSuccessful(false)) {
                if ($arguments['failOnRisky'] && !$result->allHarmless()) {
                    exit(self::FAILURE_EXIT);
                }

                if ($arguments['failOnWarning'] && $result->warningCount() > 0) {
                    exit(self::FAILURE_EXIT);
                }

                exit(self::SUCCESS_EXIT);
            }

            if ($result->errorCount() > 0) {
                exit(self::EXCEPTION_EXIT);
            }

            if ($result->failureCount() > 0) {
                exit(self::FAILURE_EXIT);
            }
        }

        return $result;
    }

    /**
     * @param PHPUnit_TextUI_ResultPrinter $resultPrinter
     */
    public function setPrinter(PHPUnit_TextUI_ResultPrinter $resultPrinter)
    {
        $this->printer = $resultPrinter;
    }

    /**
     * Override to define how to handle a failed loading of
     * a test suite.
     *
     * @param string $message
     */
    protected function runFailed($message)
    {
        $this->write($message . PHP_EOL);
        exit(self::FAILURE_EXIT);
    }

    /**
     * @param string $buffer
     */
    protected function write($buffer)
    {
        if (PHP_SAPI != 'cli' && PHP_SAPI != 'phpdbg') {
            $buffer = htmlspecialchars($buffer);
        }

        if ($this->printer !== null) {
            $this->printer->write($buffer);
        } else {
            print $buffer;
        }
    }

    /**
     * Returns the loader to be used.
     *
     * @return PHPUnit_Runner_TestSuiteLoader
     */
    public function getLoader()
    {
        if ($this->loader === null) {
            $this->loader = new PHPUnit_Runner_StandardTestSuiteLoader;
        }

        return $this->loader;
    }

    /**
     * @param array $arguments
     */
    protected function handleConfiguration(array &$arguments)
    {
        if (isset($arguments['configuration']) &&
            !$arguments['configuration'] instanceof PHPUnit_Util_Configuration) {
            $arguments['configuration'] = PHPUnit_Util_Configuration::getInstance(
                $arguments['configuration']
            );
        }

        $arguments['debug']     = isset($arguments['debug'])     ? $arguments['debug']     : false;
        $arguments['filter']    = isset($arguments['filter'])    ? $arguments['filter']    : false;
        $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : [];

        if (isset($arguments['configuration'])) {
            $arguments['configuration']->handlePHPConfiguration();

            $phpunitConfiguration = $arguments['configuration']->getPHPUnitConfiguration();

            if (isset($phpunitConfiguration['deprecatedCheckForUnintentionallyCoveredCodeSettingUsed'])) {
                $arguments['deprecatedCheckForUnintentionallyCoveredCodeSettingUsed'] = true;
            }

            if (isset($phpunitConfiguration['backupGlobals']) &&
                !isset($arguments['backupGlobals'])) {
                $arguments['backupGlobals'] = $phpunitConfiguration['backupGlobals'];
            }

            if (isset($phpunitConfiguration['backupStaticAttributes']) &&
                !isset($arguments['backupStaticAttributes'])) {
                $arguments['backupStaticAttributes'] = $phpunitConfiguration['backupStaticAttributes'];
            }

            if (isset($phpunitConfiguration['beStrictAboutChangesToGlobalState']) &&
                !isset($arguments['beStrictAboutChangesToGlobalState'])) {
                $arguments['beStrictAboutChangesToGlobalState'] = $phpunitConfiguration['beStrictAboutChangesToGlobalState'];
            }

            if (isset($phpunitConfiguration['bootstrap']) &&
                !isset($arguments['bootstrap'])) {
                $arguments['bootstrap'] = $phpunitConfiguration['bootstrap'];
            }

            if (isset($phpunitConfiguration['cacheTokens']) &&
                !isset($arguments['cacheTokens'])) {
                $arguments['cacheTokens'] = $phpunitConfiguration['cacheTokens'];
            }

            if (isset($phpunitConfiguration['colors']) &&
                !isset($arguments['colors'])) {
                $arguments['colors'] = $phpunitConfiguration['colors'];
            }

            if (isset($phpunitConfiguration['convertErrorsToExceptions']) &&
                !isset($arguments['convertErrorsToExceptions'])) {
                $arguments['convertErrorsToExceptions'] = $phpunitConfiguration['convertErrorsToExceptions'];
            }

            if (isset($phpunitConfiguration['convertNoticesToExceptions']) &&
                !isset($arguments['convertNoticesToExceptions'])) {
                $arguments['convertNoticesToExceptions'] = $phpunitConfiguration['convertNoticesToExceptions'];
            }

            if (isset($phpunitConfiguration['convertWarningsToExceptions']) &&
                !isset($arguments['convertWarningsToExceptions'])) {
                $arguments['convertWarningsToExceptions'] = $phpunitConfiguration['convertWarningsToExceptions'];
            }

            if (isset($phpunitConfiguration['processIsolation']) &&
                !isset($arguments['processIsolation'])) {
                $arguments['processIsolation'] = $phpunitConfiguration['processIsolation'];
            }

            if (isset($phpunitConfiguration['stopOnError']) &&
                !isset($arguments['stopOnError'])) {
                $arguments['stopOnError'] = $phpunitConfiguration['stopOnError'];
            }

            if (isset($phpunitConfiguration['stopOnFailure']) &&
                !isset($arguments['stopOnFailure'])) {
                $arguments['stopOnFailure'] = $phpunitConfiguration['stopOnFailure'];
            }

            if (isset($phpunitConfiguration['stopOnWarning']) &&
                !isset($arguments['stopOnWarning'])) {
                $arguments['stopOnWarning'] = $phpunitConfiguration['stopOnWarning'];
            }

            if (isset($phpunitConfiguration['stopOnIncomplete']) &&
                !isset($arguments['stopOnIncomplete'])) {
                $arguments['stopOnIncomplete'] = $phpunitConfiguration['stopOnIncomplete'];
            }

            if (isset($phpunitConfiguration['stopOnRisky']) &&
                !isset($arguments['stopOnRisky'])) {
                $arguments['stopOnRisky'] = $phpunitConfiguration['stopOnRisky'];
            }

            if (isset($phpunitConfiguration['stopOnSkipped']) &&
                !isset($arguments['stopOnSkipped'])) {
                $arguments['stopOnSkipped'] = $phpunitConfiguration['stopOnSkipped'];
            }

            if (isset($phpunitConfiguration['failOnWarning']) &&
                !isset($arguments['failOnWarning'])) {
                $arguments['failOnWarning'] = $phpunitConfiguration['failOnWarning'];
            }

            if (isset($phpunitConfiguration['failOnRisky']) &&
                !isset($arguments['failOnRisky'])) {
                $arguments['failOnRisky'] = $phpunitConfiguration['failOnRisky'];
            }

            if (isset($phpunitConfiguration['timeoutForSmallTests']) &&
                !isset($arguments['timeoutForSmallTests'])) {
                $arguments['timeoutForSmallTests'] = $phpunitConfiguration['timeoutForSmallTests'];
            }

            if (isset($phpunitConfiguration['timeoutForMediumTests']) &&
                !isset($arguments['timeoutForMediumTests'])) {
                $arguments['timeoutForMediumTests'] = $phpunitConfiguration['timeoutForMediumTests'];
            }

            if (isset($phpunitConfiguration['timeoutForLargeTests']) &&
                !isset($arguments['timeoutForLargeTests'])) {
                $arguments['timeoutForLargeTests'] = $phpunitConfiguration['timeoutForLargeTests'];
            }

            if (isset($phpunitConfiguration['reportUselessTests']) &&
                !isset($arguments['reportUselessTests'])) {
                $arguments['reportUselessTests'] = $phpunitConfiguration['reportUselessTests'];
            }

            if (isset($phpunitConfiguration['strictCoverage']) &&
                !isset($arguments['strictCoverage'])) {
                $arguments['strictCoverage'] = $phpunitConfiguration['strictCoverage'];
            }

            if (isset($phpunitConfiguration['disallowTestOutput']) &&
                !isset($arguments['disallowTestOutput'])) {
                $arguments['disallowTestOutput'] = $phpunitConfiguration['disallowTestOutput'];
            }

            if (isset($phpunitConfiguration['enforceTimeLimit']) &&
                !isset($arguments['enforceTimeLimit'])) {
                $arguments['enforceTimeLimit'] = $phpunitConfiguration['enforceTimeLimit'];
            }

            if (isset($phpunitConfiguration['disallowTodoAnnotatedTests']) &&
                !isset($arguments['disallowTodoAnnotatedTests'])) {
                $arguments['disallowTodoAnnotatedTests'] = $phpunitConfiguration['disallowTodoAnnotatedTests'];
            }

            if (isset($phpunitConfiguration['beStrictAboutResourceUsageDuringSmallTests']) &&
                !isset($arguments['beStrictAboutResourceUsageDuringSmallTests'])) {
                $arguments['beStrictAboutResourceUsageDuringSmallTests'] = $phpunitConfiguration['beStrictAboutResourceUsageDuringSmallTests'];
            }

            if (isset($phpunitConfiguration['verbose']) &&
                !isset($arguments['verbose'])) {
                $arguments['verbose'] = $phpunitConfiguration['verbose'];
            }

            if (isset($phpunitConfiguration['reverseDefectList']) &&
                !isset($arguments['reverseList'])) {
                $arguments['reverseList'] = $phpunitConfiguration['reverseDefectList'];
            }

            if (isset($phpunitConfiguration['forceCoversAnnotation']) &&
                !isset($arguments['forceCoversAnnotation'])) {
                $arguments['forceCoversAnnotation'] = $phpunitConfiguration['forceCoversAnnotation'];
            }

            if (isset($phpunitConfiguration['disableCodeCoverageIgnore']) &&
                !isset($arguments['disableCodeCoverageIgnore'])) {
                $arguments['disableCodeCoverageIgnore'] = $phpunitConfiguration['disableCodeCoverageIgnore'];
            }

            if (isset($phpunitConfiguration['registerMockObjectsFromTestArgumentsRecursively']) &&
                !isset($arguments['registerMockObjectsFromTestArgumentsRecursively'])) {
                $arguments['registerMockObjectsFromTestArgumentsRecursively'] = $phpunitConfiguration['registerMockObjectsFromTestArgumentsRecursively'];
            }

            $groupCliArgs = [];

            if (!empty($arguments['groups'])) {
                $groupCliArgs = $arguments['groups'];
            }

            $groupConfiguration = $arguments['configuration']->getGroupConfiguration();

            if (!empty($groupConfiguration['include']) &&
                !isset($arguments['groups'])) {
                $arguments['groups'] = $groupConfiguration['include'];
            }

            if (!empty($groupConfiguration['exclude']) &&
                !isset($arguments['excludeGroups'])) {
                $arguments['excludeGroups'] = array_diff($groupConfiguration['exclude'], $groupCliArgs);
            }

            foreach ($arguments['configuration']->getListenerConfiguration() as $listener) {
                if (!class_exists($listener['class'], false) &&
                    $listener['file'] !== '') {
                    require_once $listener['file'];
                }

                if (!class_exists($listener['class'])) {
                    throw new PHPUnit_Framework_Exception(
                        sprintf(
                            'Class "%s" does not exist',
                            $listener['class']
                        )
                    );
                }

                $listenerClass = new ReflectionClass($listener['class']);

                if (!$listenerClass->implementsInterface(PHPUnit_Framework_TestListener::class)) {
                    throw new PHPUnit_Framework_Exception(
                        sprintf(
                            'Class "%s" does not implement the PHPUnit_Framework_TestListener interface',
                            $listener['class']
                        )
                    );
                }

                if (count($listener['arguments']) == 0) {
                    $listener = new $listener['class'];
                } else {
                    $listener = $listenerClass->newInstanceArgs(
                        $listener['arguments']
                    );
                }

                $arguments['listeners'][] = $listener;
            }

            $loggingConfiguration = $arguments['configuration']->getLoggingConfiguration();

            if (isset($loggingConfiguration['coverage-clover']) &&
                !isset($arguments['coverageClover'])) {
                $arguments['coverageClover'] = $loggingConfiguration['coverage-clover'];
            }

            if (isset($loggingConfiguration['coverage-crap4j']) &&
                !isset($arguments['coverageCrap4J'])) {
                $arguments['coverageCrap4J'] = $loggingConfiguration['coverage-crap4j'];

                if (isset($loggingConfiguration['crap4jThreshold']) &&
                    !isset($arguments['crap4jThreshold'])) {
                    $arguments['crap4jThreshold'] = $loggingConfiguration['crap4jThreshold'];
                }
            }

            if (isset($loggingConfiguration['coverage-html']) &&
                !isset($arguments['coverageHtml'])) {
                if (isset($loggingConfiguration['lowUpperBound']) &&
                    !isset($arguments['reportLowUpperBound'])) {
                    $arguments['reportLowUpperBound'] = $loggingConfiguration['lowUpperBound'];
                }

                if (isset($loggingConfiguration['highLowerBound']) &&
                    !isset($arguments['reportHighLowerBound'])) {
                    $arguments['reportHighLowerBound'] = $loggingConfiguration['highLowerBound'];
                }

                $arguments['coverageHtml'] = $loggingConfiguration['coverage-html'];
            }

            if (isset($loggingConfiguration['coverage-php']) &&
                !isset($arguments['coveragePHP'])) {
                $arguments['coveragePHP'] = $loggingConfiguration['coverage-php'];
            }

            if (isset($loggingConfiguration['coverage-text']) &&
                !isset($arguments['coverageText'])) {
                $arguments['coverageText'] = $loggingConfiguration['coverage-text'];
                if (isset($loggingConfiguration['coverageTextShowUncoveredFiles'])) {
                    $arguments['coverageTextShowUncoveredFiles'] = $loggingConfiguration['coverageTextShowUncoveredFiles'];
                } else {
                    $arguments['coverageTextShowUncoveredFiles'] = false;
                }
                if (isset($loggingConfiguration['coverageTextShowOnlySummary'])) {
                    $arguments['coverageTextShowOnlySummary'] = $loggingConfiguration['coverageTextShowOnlySummary'];
                } else {
                    $arguments['coverageTextShowOnlySummary'] = false;
                }
            }

            if (isset($loggingConfiguration['coverage-xml']) &&
                !isset($arguments['coverageXml'])) {
                $arguments['coverageXml'] = $loggingConfiguration['coverage-xml'];
            }

            if (isset($loggingConfiguration['json']) &&
                !isset($arguments['jsonLogfile'])) {
                $arguments['jsonLogfile'] = $loggingConfiguration['json'];
            }

            if (isset($loggingConfiguration['plain'])) {
                $arguments['listeners'][] = new PHPUnit_TextUI_ResultPrinter(
                    $loggingConfiguration['plain'],
                    true
                );
            }

            if (isset($loggingConfiguration['tap']) &&
                !isset($arguments['tapLogfile'])) {
                $arguments['tapLogfile'] = $loggingConfiguration['tap'];
            }

            if (isset($loggingConfiguration['teamcity']) &&
                !isset($arguments['teamcityLogfile'])) {
                $arguments['teamcityLogfile'] = $loggingConfiguration['teamcity'];
            }

            if (isset($loggingConfiguration['junit']) &&
                !isset($arguments['junitLogfile'])) {
                $arguments['junitLogfile'] = $loggingConfiguration['junit'];

                if (isset($loggingConfiguration['logIncompleteSkipped']) &&
                    !isset($arguments['logIncompleteSkipped'])) {
                    $arguments['logIncompleteSkipped'] = $loggingConfiguration['logIncompleteSkipped'];
                }
            }

            if (isset($loggingConfiguration['testdox-html']) &&
                !isset($arguments['testdoxHTMLFile'])) {
                $arguments['testdoxHTMLFile'] = $loggingConfiguration['testdox-html'];
            }

            if (isset($loggingConfiguration['testdox-text']) &&
                !isset($arguments['testdoxTextFile'])) {
                $arguments['testdoxTextFile'] = $loggingConfiguration['testdox-text'];
            }

            if (isset($loggingConfiguration['testdox-xml']) &&
                !isset($arguments['testdoxXMLFile'])) {
                $arguments['testdoxXMLFile'] = $loggingConfiguration['testdox-xml'];
            }

            $testdoxGroupConfiguration = $arguments['configuration']->getTestdoxGroupConfiguration();

            if (isset($testdoxGroupConfiguration['include']) &&
                !isset($arguments['testdoxGroups'])) {
                $arguments['testdoxGroups'] = $testdoxGroupConfiguration['include'];
            }

            if (isset($testdoxGroupConfiguration['exclude']) &&
                !isset($arguments['testdoxExcludeGroups'])) {
                $arguments['testdoxExcludeGroups'] = $testdoxGroupConfiguration['exclude'];
            }
        }

        $arguments['addUncoveredFilesFromWhitelist']                  = isset($arguments['addUncoveredFilesFromWhitelist'])                  ? $arguments['addUncoveredFilesFromWhitelist']                  : true;
        $arguments['processUncoveredFilesFromWhitelist']              = isset($arguments['processUncoveredFilesFromWhitelist'])              ? $arguments['processUncoveredFilesFromWhitelist']              : false;
        $arguments['backupGlobals']                                   = isset($arguments['backupGlobals'])                                   ? $arguments['backupGlobals']                                   : null;
        $arguments['backupStaticAttributes']                          = isset($arguments['backupStaticAttributes'])                          ? $arguments['backupStaticAttributes']                          : null;
        $arguments['beStrictAboutChangesToGlobalState']               = isset($arguments['beStrictAboutChangesToGlobalState'])               ? $arguments['beStrictAboutChangesToGlobalState']               : null;
        $arguments['cacheTokens']                                     = isset($arguments['cacheTokens'])                                     ? $arguments['cacheTokens']                                     : false;
        $arguments['columns']                                         = isset($arguments['columns'])                                         ? $arguments['columns']                                         : 80;
        $arguments['colors']                                          = isset($arguments['colors'])                                          ? $arguments['colors']                                          : PHPUnit_TextUI_ResultPrinter::COLOR_DEFAULT;
        $arguments['convertErrorsToExceptions']                       = isset($arguments['convertErrorsToExceptions'])                       ? $arguments['convertErrorsToExceptions']                       : true;
        $arguments['convertNoticesToExceptions']                      = isset($arguments['convertNoticesToExceptions'])                      ? $arguments['convertNoticesToExceptions']                      : true;
        $arguments['convertWarningsToExceptions']                     = isset($arguments['convertWarningsToExceptions'])                     ? $arguments['convertWarningsToExceptions']                     : true;
        $arguments['excludeGroups']                                   = isset($arguments['excludeGroups'])                                   ? $arguments['excludeGroups']                                   : [];
        $arguments['groups']                                          = isset($arguments['groups'])                                          ? $arguments['groups']                                          : [];
        $arguments['logIncompleteSkipped']                            = isset($arguments['logIncompleteSkipped'])                            ? $arguments['logIncompleteSkipped']                            : false;
        $arguments['processIsolation']                                = isset($arguments['processIsolation'])                                ? $arguments['processIsolation']                                : false;
        $arguments['repeat']                                          = isset($arguments['repeat'])                                          ? $arguments['repeat']                                          : false;
        $arguments['reportHighLowerBound']                            = isset($arguments['reportHighLowerBound'])                            ? $arguments['reportHighLowerBound']                            : 90;
        $arguments['reportLowUpperBound']                             = isset($arguments['reportLowUpperBound'])                             ? $arguments['reportLowUpperBound']                             : 50;
        $arguments['crap4jThreshold']                                 = isset($arguments['crap4jThreshold'])                                 ? $arguments['crap4jThreshold']                                 : 30;
        $arguments['stopOnError']                                     = isset($arguments['stopOnError'])                                     ? $arguments['stopOnError']                                     : false;
        $arguments['stopOnFailure']                                   = isset($arguments['stopOnFailure'])                                   ? $arguments['stopOnFailure']                                   : false;
        $arguments['stopOnWarning']                                   = isset($arguments['stopOnWarning'])                                   ? $arguments['stopOnWarning']                                   : false;
        $arguments['stopOnIncomplete']                                = isset($arguments['stopOnIncomplete'])                                ? $arguments['stopOnIncomplete']                                : false;
        $arguments['stopOnRisky']                                     = isset($arguments['stopOnRisky'])                                     ? $arguments['stopOnRisky']                                     : false;
        $arguments['stopOnSkipped']                                   = isset($arguments['stopOnSkipped'])                                   ? $arguments['stopOnSkipped']                                   : false;
        $arguments['failOnWarning']                                   = isset($arguments['failOnWarning'])                                   ? $arguments['failOnWarning']                                   : false;
        $arguments['failOnRisky']                                     = isset($arguments['failOnRisky'])                                     ? $arguments['failOnRisky']                                     : false;
        $arguments['timeoutForSmallTests']                            = isset($arguments['timeoutForSmallTests'])                            ? $arguments['timeoutForSmallTests']                            : 1;
        $arguments['timeoutForMediumTests']                           = isset($arguments['timeoutForMediumTests'])                           ? $arguments['timeoutForMediumTests']                           : 10;
        $arguments['timeoutForLargeTests']                            = isset($arguments['timeoutForLargeTests'])                            ? $arguments['timeoutForLargeTests']                            : 60;
        $arguments['reportUselessTests']                              = isset($arguments['reportUselessTests'])                              ? $arguments['reportUselessTests']                              : false;
        $arguments['strictCoverage']                                  = isset($arguments['strictCoverage'])                                  ? $arguments['strictCoverage']                                  : false;
        $arguments['disallowTestOutput']                              = isset($arguments['disallowTestOutput'])                              ? $arguments['disallowTestOutput']                              : false;
        $arguments['enforceTimeLimit']                                = isset($arguments['enforceTimeLimit'])                                ? $arguments['enforceTimeLimit']                                : false;
        $arguments['disallowTodoAnnotatedTests']                      = isset($arguments['disallowTodoAnnotatedTests'])                      ? $arguments['disallowTodoAnnotatedTests']                      : false;
        $arguments['beStrictAboutResourceUsageDuringSmallTests']      = isset($arguments['beStrictAboutResourceUsageDuringSmallTests'])      ? $arguments['beStrictAboutResourceUsageDuringSmallTests']      : false;
        $arguments['reverseList']                                     = isset($arguments['reverseList'])                                     ? $arguments['reverseList']                                     : false;
        $arguments['registerMockObjectsFromTestArgumentsRecursively'] = isset($arguments['registerMockObjectsFromTestArgumentsRecursively']) ? $arguments['registerMockObjectsFromTestArgumentsRecursively'] : false;
        $arguments['verbose']                                         = isset($arguments['verbose'])                                         ? $arguments['verbose']                                         : false;
        $arguments['testdoxExcludeGroups']                            = isset($arguments['testdoxExcludeGroups'])                            ? $arguments['testdoxExcludeGroups']                            : [];
        $arguments['testdoxGroups']                                   = isset($arguments['testdoxGroups'])                                   ? $arguments['testdoxGroups']                                   : [];
    }

    /**
     * @param string $type
     * @param string $message
     */
    private function writeMessage($type, $message)
    {
        if (!$this->messagePrinted) {
            $this->write("\n");
        }

        $this->write(
            sprintf(
                "%-15s%s\n",
                $type . ':',
                $message
            )
        );

        $this->messagePrinted = true;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Utility class for blacklisting PHPUnit's own source code files.
 */
class PHPUnit_Util_Blacklist
{
    /**
     * @var array
     */
    public static $blacklistedClassNames = [
        'File_Iterator'                               => 1,
        'PHP_Invoker'                                 => 1,
        'PHP_Timer'                                   => 1,
        'PHP_Token'                                   => 1,
        'PHPUnit_Framework_TestCase'                  => 2,
        'PHPUnit_Extensions_Database_TestCase'        => 2,
        'PHPUnit_Framework_MockObject_Generator'      => 2,
        'Text_Template'                               => 1,
        'Symfony\Component\Yaml\Yaml'                 => 1,
        'SebastianBergmann\CodeCoverage\CodeCoverage' => 1,
        'SebastianBergmann\Diff\Diff'                 => 1,
        'SebastianBergmann\Environment\Runtime'       => 1,
        'SebastianBergmann\Comparator\Comparator'     => 1,
        'SebastianBergmann\Exporter\Exporter'         => 1,
        'SebastianBergmann\GlobalState\Snapshot'      => 1,
        'SebastianBergmann\RecursionContext\Context'  => 1,
        'SebastianBergmann\Version'                   => 1,
        'Composer\Autoload\ClassLoader'               => 1,
        'Doctrine\Instantiator\Instantiator'          => 1,
        'phpDocumentor\Reflection\DocBlock'           => 1,
        'Prophecy\Prophet'                            => 1,
        'DeepCopy\DeepCopy'                           => 1
    ];

    /**
     * @var array
     */
    private static $directories;

    /**
     * @return array
     */
    public function getBlacklistedDirectories()
    {
        $this->initialize();

        return self::$directories;
    }

    /**
     * @param string $file
     *
     * @return bool
     */
    public function isBlacklisted($file)
    {
        if (defined('PHPUNIT_TESTSUITE')) {
            return false;
        }

        $this->initialize();

        foreach (self::$directories as $directory) {
            if (strpos($file, $directory) === 0) {
                return true;
            }
        }

        return false;
    }

    private function initialize()
    {
        if (self::$directories === null) {
            self::$directories = [];

            foreach (self::$blacklistedClassNames as $className => $parent) {
                if (!class_exists($className)) {
                    continue;
                }

                $reflector = new ReflectionClass($className);
                $directory = $reflector->getFileName();

                for ($i = 0; $i < $parent; $i++) {
                    $directory = dirname($directory);
                }

                self::$directories[] = $directory;
            }

            // Hide process isolation workaround on Windows.
            // @see PHPUnit_Util_PHP::factory()
            // @see PHPUnit_Util_PHP_Windows::process()
            if (DIRECTORY_SEPARATOR === '\\') {
                // tempnam() prefix is limited to first 3 chars.
                // @see http://php.net/manual/en/function.tempnam.php
                self::$directories[] = sys_get_temp_dir() . '\\PHP';
            }
        }
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Wrapper for the PHPUnit XML configuration file.
 *
 * Example XML configuration file:
 * <code>
 * <?xml version="1.0" encoding="utf-8" ?>
 *
 * <phpunit backupGlobals="true"
 *          backupStaticAttributes="false"
 *          bootstrap="/path/to/bootstrap.php"
 *          cacheTokens="false"
 *          columns="80"
 *          colors="false"
 *          stderr="false"
 *          convertErrorsToExceptions="true"
 *          convertNoticesToExceptions="true"
 *          convertWarningsToExceptions="true"
 *          forceCoversAnnotation="false"
 *          processIsolation="false"
 *          stopOnError="false"
 *          stopOnFailure="false"
 *          stopOnWarning="false"
 *          stopOnIncomplete="false"
 *          stopOnRisky="false"
 *          stopOnSkipped="false"
 *          failOnWarning="false"
 *          failOnRisky="false"
 *          extensionsDirectory="tools/phpunit.d"
 *          printerClass="PHPUnit_TextUI_ResultPrinter"
 *          testSuiteLoaderClass="PHPUnit_Runner_StandardTestSuiteLoader"
 *          beStrictAboutChangesToGlobalState="false"
 *          beStrictAboutCoversAnnotation="false"
 *          beStrictAboutOutputDuringTests="false"
 *          beStrictAboutResourceUsageDuringSmallTests="false"
 *          beStrictAboutTestsThatDoNotTestAnything="false"
 *          beStrictAboutTodoAnnotatedTests="false"
 *          checkForUnintentionallyCoveredCode="false"
 *          enforceTimeLimit="false"
 *          timeoutForSmallTests="1"
 *          timeoutForMediumTests="10"
 *          timeoutForLargeTests="60"
 *          verbose="false"
 *          reverseDefectList="false"
 *          registerMockObjectsFromTestArgumentsRecursively="false">
 *   <testsuites>
 *     <testsuite name="My Test Suite">
 *       <directory suffix="Test.php" phpVersion="5.3.0" phpVersionOperator=">=">/path/to/files</directory>
 *       <file phpVersion="5.3.0" phpVersionOperator=">=">/path/to/MyTest.php</file>
 *       <exclude>/path/to/files/exclude</exclude>
 *     </testsuite>
 *   </testsuites>
 *
 *   <groups>
 *     <include>
 *       <group>name</group>
 *     </include>
 *     <exclude>
 *       <group>name</group>
 *     </exclude>
 *   </groups>
 *
 *   <testdoxGroups>
 *     <include>
 *       <group>name</group>
 *     </include>
 *     <exclude>
 *       <group>name</group>
 *     </exclude>
 *   </testdoxGroups>
 *
 *   <filter>
 *     <whitelist addUncoveredFilesFromWhitelist="true"
 *                processUncoveredFilesFromWhitelist="false">
 *       <directory suffix=".php">/path/to/files</directory>
 *       <file>/path/to/file</file>
 *       <exclude>
 *         <directory suffix=".php">/path/to/files</directory>
 *         <file>/path/to/file</file>
 *       </exclude>
 *     </whitelist>
 *   </filter>
 *
 *   <listeners>
 *     <listener class="MyListener" file="/optional/path/to/MyListener.php">
 *       <arguments>
 *         <array>
 *           <element key="0">
 *             <string>Sebastian</string>
 *           </element>
 *         </array>
 *         <integer>22</integer>
 *         <string>April</string>
 *         <double>19.78</double>
 *         <null/>
 *         <object class="stdClass"/>
 *         <file>MyRelativeFile.php</file>
 *         <directory>MyRelativeDir</directory>
 *       </arguments>
 *     </listener>
 *   </listeners>
 *
 *   <logging>
 *     <log type="coverage-html" target="/tmp/report" lowUpperBound="50" highLowerBound="90"/>
 *     <log type="coverage-clover" target="/tmp/clover.xml"/>
 *     <log type="coverage-crap4j" target="/tmp/crap.xml" threshold="30"/>
 *     <log type="json" target="/tmp/logfile.json"/>
 *     <log type="plain" target="/tmp/logfile.txt"/>
 *     <log type="tap" target="/tmp/logfile.tap"/>
 *     <log type="teamcity" target="/tmp/logfile.txt"/>
 *     <log type="junit" target="/tmp/logfile.xml" logIncompleteSkipped="false"/>
 *     <log type="testdox-html" target="/tmp/testdox.html"/>
 *     <log type="testdox-text" target="/tmp/testdox.txt"/>
 *     <log type="testdox-xml" target="/tmp/testdox.xml"/>
 *   </logging>
 *
 *   <php>
 *     <includePath>.</includePath>
 *     <ini name="foo" value="bar"/>
 *     <const name="foo" value="bar"/>
 *     <var name="foo" value="bar"/>
 *     <env name="foo" value="bar"/>
 *     <post name="foo" value="bar"/>
 *     <get name="foo" value="bar"/>
 *     <cookie name="foo" value="bar"/>
 *     <server name="foo" value="bar"/>
 *     <files name="foo" value="bar"/>
 *     <request name="foo" value="bar"/>
 *   </php>
 * </phpunit>
 * </code>
 */
class PHPUnit_Util_Configuration
{
    private static $instances = [];

    protected $document;
    protected $xpath;
    protected $filename;

    /**
     * Loads a PHPUnit configuration file.
     *
     * @param string $filename
     */
    protected function __construct($filename)
    {
        $this->filename = $filename;
        $this->document = PHPUnit_Util_XML::loadFile($filename, false, true, true);
        $this->xpath    = new DOMXPath($this->document);
    }

    final private function __clone()
    {
    }

    /**
     * Returns a PHPUnit configuration object.
     *
     * @param string $filename
     *
     * @return PHPUnit_Util_Configuration
     */
    public static function getInstance($filename)
    {
        $realpath = realpath($filename);

        if ($realpath === false) {
            throw new PHPUnit_Framework_Exception(
                sprintf(
                    'Could not read "%s".',
                    $filename
                )
            );
        }

        if (!isset(self::$instances[$realpath])) {
            self::$instances[$realpath] = new self($realpath);
        }

        return self::$instances[$realpath];
    }

    /**
     * Returns the realpath to the configuration file.
     *
     * @return string
     */
    public function getFilename()
    {
        return $this->filename;
    }

    /**
     * Returns the configuration for SUT filtering.
     *
     * @return array
     */
    public function getFilterConfiguration()
    {
        $addUncoveredFilesFromWhitelist     = true;
        $processUncoveredFilesFromWhitelist = false;

        $tmp = $this->xpath->query('filter/whitelist');

        if ($tmp->length == 1) {
            if ($tmp->item(0)->hasAttribute('addUncoveredFilesFromWhitelist')) {
                $addUncoveredFilesFromWhitelist = $this->getBoolean(
                    (string) $tmp->item(0)->getAttribute(
                        'addUncoveredFilesFromWhitelist'
                    ),
                    true
                );
            }

            if ($tmp->item(0)->hasAttribute('processUncoveredFilesFromWhitelist')) {
                $processUncoveredFilesFromWhitelist = $this->getBoolean(
                    (string) $tmp->item(0)->getAttribute(
                        'processUncoveredFilesFromWhitelist'
                    ),
                    false
                );
            }
        }

        return [
          'whitelist' => [
            'addUncoveredFilesFromWhitelist'     => $addUncoveredFilesFromWhitelist,
            'processUncoveredFilesFromWhitelist' => $processUncoveredFilesFromWhitelist,
            'include'                            => [
              'directory' => $this->readFilterDirectories(
                  'filter/whitelist/directory'
              ),
              'file' => $this->readFilterFiles(
                  'filter/whitelist/file'
              )
            ],
            'exclude' => [
              'directory' => $this->readFilterDirectories(
                  'filter/whitelist/exclude/directory'
              ),
              'file' => $this->readFilterFiles(
                  'filter/whitelist/exclude/file'
              )
            ]
          ]
        ];
    }

    /**
     * Returns the configuration for groups.
     *
     * @return array
     */
    public function getGroupConfiguration()
    {
        return $this->parseGroupConfiguration('groups');
    }

    /**
     * Returns the configuration for testdox groups.
     *
     * @return array
     */
    public function getTestdoxGroupConfiguration()
    {
        return $this->parseGroupConfiguration('testdoxGroups');
    }

    /**
     * @param string $root
     *
     * @return array
     */
    private function parseGroupConfiguration($root)
    {
        $groups = [
            'include' => [],
            'exclude' => []
        ];

        foreach ($this->xpath->query($root . '/include/group') as $group) {
            $groups['include'][] = (string) $group->textContent;
        }

        foreach ($this->xpath->query($root . '/exclude/group') as $group) {
            $groups['exclude'][] = (string) $group->textContent;
        }

        return $groups;
    }

    /**
     * Returns the configuration for listeners.
     *
     * @return array
     */
    public function getListenerConfiguration()
    {
        $result = [];

        foreach ($this->xpath->query('listeners/listener') as $listener) {
            $class     = (string) $listener->getAttribute('class');
            $file      = '';
            $arguments = [];

            if ($listener->getAttribute('file')) {
                $file = $this->toAbsolutePath(
                    (string) $listener->getAttribute('file'),
                    true
                );
            }

            foreach ($listener->childNodes as $node) {
                if ($node instanceof DOMElement && $node->tagName == 'arguments') {
                    foreach ($node->childNodes as $argument) {
                        if ($argument instanceof DOMElement) {
                            if ($argument->tagName == 'file' ||
                            $argument->tagName == 'directory') {
                                $arguments[] = $this->toAbsolutePath((string) $argument->textContent);
                            } else {
                                $arguments[] = PHPUnit_Util_XML::xmlToVariable($argument);
                            }
                        }
                    }
                }
            }

            $result[] = [
              'class'     => $class,
              'file'      => $file,
              'arguments' => $arguments
            ];
        }

        return $result;
    }

    /**
     * Returns the logging configuration.
     *
     * @return array
     */
    public function getLoggingConfiguration()
    {
        $result = [];

        foreach ($this->xpath->query('logging/log') as $log) {
            $type   = (string) $log->getAttribute('type');
            $target = (string) $log->getAttribute('target');

            if (!$target) {
                continue;
            }

            $target = $this->toAbsolutePath($target);

            if ($type == 'coverage-html') {
                if ($log->hasAttribute('lowUpperBound')) {
                    $result['lowUpperBound'] = $this->getInteger(
                        (string) $log->getAttribute('lowUpperBound'),
                        50
                    );
                }

                if ($log->hasAttribute('highLowerBound')) {
                    $result['highLowerBound'] = $this->getInteger(
                        (string) $log->getAttribute('highLowerBound'),
                        90
                    );
                }
            } elseif ($type == 'coverage-crap4j') {
                if ($log->hasAttribute('threshold')) {
                    $result['crap4jThreshold'] = $this->getInteger(
                        (string) $log->getAttribute('threshold'),
                        30
                    );
                }
            } elseif ($type == 'junit') {
                if ($log->hasAttribute('logIncompleteSkipped')) {
                    $result['logIncompleteSkipped'] = $this->getBoolean(
                        (string) $log->getAttribute('logIncompleteSkipped'),
                        false
                    );
                }
            } elseif ($type == 'coverage-text') {
                if ($log->hasAttribute('showUncoveredFiles')) {
                    $result['coverageTextShowUncoveredFiles'] = $this->getBoolean(
                        (string) $log->getAttribute('showUncoveredFiles'),
                        false
                    );
                }
                if ($log->hasAttribute('showOnlySummary')) {
                    $result['coverageTextShowOnlySummary'] = $this->getBoolean(
                        (string) $log->getAttribute('showOnlySummary'),
                        false
                    );
                }
            }

            $result[$type] = $target;
        }

        return $result;
    }

    /**
     * Returns the PHP configuration.
     *
     * @return array
     */
    public function getPHPConfiguration()
    {
        $result = [
          'include_path' => [],
          'ini'          => [],
          'const'        => [],
          'var'          => [],
          'env'          => [],
          'post'         => [],
          'get'          => [],
          'cookie'       => [],
          'server'       => [],
          'files'        => [],
          'request'      => []
        ];

        foreach ($this->xpath->query('php/includePath') as $includePath) {
            $path = (string) $includePath->textContent;
            if ($path) {
                $result['include_path'][] = $this->toAbsolutePath($path);
            }
        }

        foreach ($this->xpath->query('php/ini') as $ini) {
            $name  = (string) $ini->getAttribute('name');
            $value = (string) $ini->getAttribute('value');

            $result['ini'][$name] = $value;
        }

        foreach ($this->xpath->query('php/const') as $const) {
            $name  = (string) $const->getAttribute('name');
            $value = (string) $const->getAttribute('value');

            $result['const'][$name] = $this->getBoolean($value, $value);
        }

        foreach (['var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) {
            foreach ($this->xpath->query('php/' . $array) as $var) {
                $name  = (string) $var->getAttribute('name');
                $value = (string) $var->getAttribute('value');

                $result[$array][$name] = $this->getBoolean($value, $value);
            }
        }

        return $result;
    }

    /**
     * Handles the PHP configuration.
     */
    public function handlePHPConfiguration()
    {
        $configuration = $this->getPHPConfiguration();

        if (! empty($configuration['include_path'])) {
            ini_set(
                'include_path',
                implode(PATH_SEPARATOR, $configuration['include_path']) .
                PATH_SEPARATOR .
                ini_get('include_path')
            );
        }

        foreach ($configuration['ini'] as $name => $value) {
            if (defined($value)) {
                $value = constant($value);
            }

            ini_set($name, $value);
        }

        foreach ($configuration['const'] as $name => $value) {
            if (!defined($name)) {
                define($name, $value);
            }
        }

        foreach (['var', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) {
            // See https://github.com/sebastianbergmann/phpunit/issues/277
            switch ($array) {
                case 'var':
                    $target = &$GLOBALS;
                    break;

                case 'server':
                    $target = &$_SERVER;
                    break;

                default:
                    $target = &$GLOBALS['_' . strtoupper($array)];
                    break;
            }

            foreach ($configuration[$array] as $name => $value) {
                $target[$name] = $value;
            }
        }

        foreach ($configuration['env'] as $name => $value) {
            if (false === getenv($name)) {
                putenv("{$name}={$value}");
            }
            if (!isset($_ENV[$name])) {
                $_ENV[$name] = $value;
            }
        }
    }

    /**
     * Returns the PHPUnit configuration.
     *
     * @return array
     */
    public function getPHPUnitConfiguration()
    {
        $result = [];
        $root   = $this->document->documentElement;

        if ($root->hasAttribute('cacheTokens')) {
            $result['cacheTokens'] = $this->getBoolean(
                (string) $root->getAttribute('cacheTokens'),
                false
            );
        }

        if ($root->hasAttribute('columns')) {
            $columns = (string) $root->getAttribute('columns');

            if ($columns == 'max') {
                $result['columns'] = 'max';
            } else {
                $result['columns'] = $this->getInteger($columns, 80);
            }
        }

        if ($root->hasAttribute('colors')) {
            /* only allow boolean for compatibility with previous versions
              'always' only allowed from command line */
            if ($this->getBoolean($root->getAttribute('colors'), false)) {
                $result['colors'] = PHPUnit_TextUI_ResultPrinter::COLOR_AUTO;
            } else {
                $result['colors'] = PHPUnit_TextUI_ResultPrinter::COLOR_NEVER;
            }
        }

        /*
         * Issue #657
         */
        if ($root->hasAttribute('stderr')) {
            $result['stderr'] = $this->getBoolean(
                (string) $root->getAttribute('stderr'),
                false
            );
        }

        if ($root->hasAttribute('backupGlobals')) {
            $result['backupGlobals'] = $this->getBoolean(
                (string) $root->getAttribute('backupGlobals'),
                true
            );
        }

        if ($root->hasAttribute('backupStaticAttributes')) {
            $result['backupStaticAttributes'] = $this->getBoolean(
                (string) $root->getAttribute('backupStaticAttributes'),
                false
            );
        }

        if ($root->getAttribute('bootstrap')) {
            $result['bootstrap'] = $this->toAbsolutePath(
                (string) $root->getAttribute('bootstrap')
            );
        }

        if ($root->hasAttribute('convertErrorsToExceptions')) {
            $result['convertErrorsToExceptions'] = $this->getBoolean(
                (string) $root->getAttribute('convertErrorsToExceptions'),
                true
            );
        }

        if ($root->hasAttribute('convertNoticesToExceptions')) {
            $result['convertNoticesToExceptions'] = $this->getBoolean(
                (string) $root->getAttribute('convertNoticesToExceptions'),
                true
            );
        }

        if ($root->hasAttribute('convertWarningsToExceptions')) {
            $result['convertWarningsToExceptions'] = $this->getBoolean(
                (string) $root->getAttribute('convertWarningsToExceptions'),
                true
            );
        }

        if ($root->hasAttribute('forceCoversAnnotation')) {
            $result['forceCoversAnnotation'] = $this->getBoolean(
                (string) $root->getAttribute('forceCoversAnnotation'),
                false
            );
        }

        if ($root->hasAttribute('disableCodeCoverageIgnore')) {
            $result['disableCodeCoverageIgnore'] = $this->getBoolean(
                (string) $root->getAttribute('disableCodeCoverageIgnore'),
                false
            );
        }

        if ($root->hasAttribute('processIsolation')) {
            $result['processIsolation'] = $this->getBoolean(
                (string) $root->getAttribute('processIsolation'),
                false
            );
        }

        if ($root->hasAttribute('stopOnError')) {
            $result['stopOnError'] = $this->getBoolean(
                (string) $root->getAttribute('stopOnError'),
                false
            );
        }

        if ($root->hasAttribute('stopOnFailure')) {
            $result['stopOnFailure'] = $this->getBoolean(
                (string) $root->getAttribute('stopOnFailure'),
                false
            );
        }

        if ($root->hasAttribute('stopOnWarning')) {
            $result['stopOnWarning'] = $this->getBoolean(
                (string) $root->getAttribute('stopOnWarning'),
                false
            );
        }

        if ($root->hasAttribute('stopOnIncomplete')) {
            $result['stopOnIncomplete'] = $this->getBoolean(
                (string) $root->getAttribute('stopOnIncomplete'),
                false
            );
        }

        if ($root->hasAttribute('stopOnRisky')) {
            $result['stopOnRisky'] = $this->getBoolean(
                (string) $root->getAttribute('stopOnRisky'),
                false
            );
        }

        if ($root->hasAttribute('stopOnSkipped')) {
            $result['stopOnSkipped'] = $this->getBoolean(
                (string) $root->getAttribute('stopOnSkipped'),
                false
            );
        }

        if ($root->hasAttribute('failOnWarning')) {
            $result['failOnWarning'] = $this->getBoolean(
                (string) $root->getAttribute('failOnWarning'),
                false
            );
        }

        if ($root->hasAttribute('failOnRisky')) {
            $result['failOnRisky'] = $this->getBoolean(
                (string) $root->getAttribute('failOnRisky'),
                false
            );
        }

        if ($root->hasAttribute('testSuiteLoaderClass')) {
            $result['testSuiteLoaderClass'] = (string) $root->getAttribute(
                'testSuiteLoaderClass'
            );
        }

        if ($root->getAttribute('testSuiteLoaderFile')) {
            $result['testSuiteLoaderFile'] = $this->toAbsolutePath(
                (string) $root->getAttribute('testSuiteLoaderFile')
            );
        }

        if ($root->hasAttribute('printerClass')) {
            $result['printerClass'] = (string) $root->getAttribute(
                'printerClass'
            );
        }

        if ($root->getAttribute('printerFile')) {
            $result['printerFile'] = $this->toAbsolutePath(
                (string) $root->getAttribute('printerFile')
            );
        }

        if ($root->hasAttribute('beStrictAboutChangesToGlobalState')) {
            $result['beStrictAboutChangesToGlobalState'] = $this->getBoolean(
                (string) $root->getAttribute('beStrictAboutChangesToGlobalState'),
                false
            );
        }

        if ($root->hasAttribute('beStrictAboutOutputDuringTests')) {
            $result['disallowTestOutput'] = $this->getBoolean(
                (string) $root->getAttribute('beStrictAboutOutputDuringTests'),
                false
            );
        }

        if ($root->hasAttribute('beStrictAboutResourceUsageDuringSmallTests')) {
            $result['beStrictAboutResourceUsageDuringSmallTests'] = $this->getBoolean(
                (string) $root->getAttribute('beStrictAboutResourceUsageDuringSmallTests'),
                false
            );
        }

        if ($root->hasAttribute('beStrictAboutTestsThatDoNotTestAnything')) {
            $result['reportUselessTests'] = $this->getBoolean(
                (string) $root->getAttribute('beStrictAboutTestsThatDoNotTestAnything'),
                false
            );
        }

        if ($root->hasAttribute('beStrictAboutTodoAnnotatedTests')) {
            $result['disallowTodoAnnotatedTests'] = $this->getBoolean(
                (string) $root->getAttribute('beStrictAboutTodoAnnotatedTests'),
                false
            );
        }

        if ($root->hasAttribute('beStrictAboutCoversAnnotation')) {
            $result['strictCoverage'] = $this->getBoolean(
                (string) $root->getAttribute('beStrictAboutCoversAnnotation'),
                false
            );
        } elseif ($root->hasAttribute('checkForUnintentionallyCoveredCode')) {
            $result['strictCoverage'] = $this->getBoolean(
                (string) $root->getAttribute('checkForUnintentionallyCoveredCode'),
                false
            );

            $result['deprecatedCheckForUnintentionallyCoveredCodeSettingUsed'] = true;
        }

        if ($root->hasAttribute('enforceTimeLimit')) {
            $result['enforceTimeLimit'] = $this->getBoolean(
                (string) $root->getAttribute('enforceTimeLimit'),
                false
            );
        }

        if ($root->hasAttribute('timeoutForSmallTests')) {
            $result['timeoutForSmallTests'] = $this->getInteger(
                (string) $root->getAttribute('timeoutForSmallTests'),
                1
            );
        }

        if ($root->hasAttribute('timeoutForMediumTests')) {
            $result['timeoutForMediumTests'] = $this->getInteger(
                (string) $root->getAttribute('timeoutForMediumTests'),
                10
            );
        }

        if ($root->hasAttribute('timeoutForLargeTests')) {
            $result['timeoutForLargeTests'] = $this->getInteger(
                (string) $root->getAttribute('timeoutForLargeTests'),
                60
            );
        }

        if ($root->hasAttribute('reverseDefectList')) {
            $result['reverseDefectList'] = $this->getBoolean(
                (string) $root->getAttribute('reverseDefectList'),
                false
            );
        }

        if ($root->hasAttribute('verbose')) {
            $result['verbose'] = $this->getBoolean(
                (string) $root->getAttribute('verbose'),
                false
            );
        }

        if ($root->hasAttribute('registerMockObjectsFromTestArgumentsRecursively')) {
            $result['registerMockObjectsFromTestArgumentsRecursively'] = $this->getBoolean(
                (string) $root->getAttribute('registerMockObjectsFromTestArgumentsRecursively'),
                false
            );
        }

        if ($root->hasAttribute('extensionsDirectory')) {
            $result['extensionsDirectory'] = $this->toAbsolutePath(
                    (string) $root->getAttribute(
                        'extensionsDirectory'
                    )
            );
        }

        return $result;
    }

    /**
     * Returns the test suite configuration.
     *
     * @return PHPUnit_Framework_TestSuite
     */
    public function getTestSuiteConfiguration($testSuiteFilter = null)
    {
        $testSuiteNodes = $this->xpath->query('testsuites/testsuite');

        if ($testSuiteNodes->length == 0) {
            $testSuiteNodes = $this->xpath->query('testsuite');
        }

        if ($testSuiteNodes->length == 1) {
            return $this->getTestSuite($testSuiteNodes->item(0), $testSuiteFilter);
        }

        if ($testSuiteNodes->length > 1) {
            $suite = new PHPUnit_Framework_TestSuite;

            foreach ($testSuiteNodes as $testSuiteNode) {
                $suite->addTestSuite(
                    $this->getTestSuite($testSuiteNode, $testSuiteFilter)
                );
            }

            return $suite;
        }
    }

    /**
     * Returns the test suite names from the configuration.
     *
     * @return array
     */
    public function getTestSuiteNames()
    {
        $names = [];
        $nodes = $this->xpath->query('*/testsuite');
        foreach ($nodes as $node) {
            $names[] = $node->getAttribute('name');
        }

        return $names;
    }

    /**
     * @param DOMElement $testSuiteNode
     *
     * @return PHPUnit_Framework_TestSuite
     */
    protected function getTestSuite(DOMElement $testSuiteNode, $testSuiteFilter = null)
    {
        if ($testSuiteNode->hasAttribute('name')) {
            $suite = new PHPUnit_Framework_TestSuite(
                (string) $testSuiteNode->getAttribute('name')
            );
        } else {
            $suite = new PHPUnit_Framework_TestSuite;
        }

        $exclude = [];

        foreach ($testSuiteNode->getElementsByTagName('exclude') as $excludeNode) {
            $excludeFile = (string) $excludeNode->textContent;
            if ($excludeFile) {
                $exclude[] = $this->toAbsolutePath($excludeFile);
            }
        }

        $fileIteratorFacade = new File_Iterator_Facade;

        foreach ($testSuiteNode->getElementsByTagName('directory') as $directoryNode) {
            if ($testSuiteFilter && $directoryNode->parentNode->getAttribute('name') != $testSuiteFilter) {
                continue;
            }

            $directory = (string) $directoryNode->textContent;

            if (empty($directory)) {
                continue;
            }

            if ($directoryNode->hasAttribute('phpVersion')) {
                $phpVersion = (string) $directoryNode->getAttribute('phpVersion');
            } else {
                $phpVersion = PHP_VERSION;
            }

            if ($directoryNode->hasAttribute('phpVersionOperator')) {
                $phpVersionOperator = (string) $directoryNode->getAttribute('phpVersionOperator');
            } else {
                $phpVersionOperator = '>=';
            }

            if (!version_compare(PHP_VERSION, $phpVersion, $phpVersionOperator)) {
                continue;
            }

            if ($directoryNode->hasAttribute('prefix')) {
                $prefix = (string) $directoryNode->getAttribute('prefix');
            } else {
                $prefix = '';
            }

            if ($directoryNode->hasAttribute('suffix')) {
                $suffix = (string) $directoryNode->getAttribute('suffix');
            } else {
                $suffix = 'Test.php';
            }

            $files = $fileIteratorFacade->getFilesAsArray(
                $this->toAbsolutePath($directory),
                $suffix,
                $prefix,
                $exclude
            );
            $suite->addTestFiles($files);
        }

        foreach ($testSuiteNode->getElementsByTagName('file') as $fileNode) {
            if ($testSuiteFilter && $fileNode->parentNode->getAttribute('name') != $testSuiteFilter) {
                continue;
            }

            $file = (string) $fileNode->textContent;

            if (empty($file)) {
                continue;
            }

            // Get the absolute path to the file
            $file = $fileIteratorFacade->getFilesAsArray(
                $this->toAbsolutePath($file)
            );

            if (!isset($file[0])) {
                continue;
            }

            $file = $file[0];

            if ($fileNode->hasAttribute('phpVersion')) {
                $phpVersion = (string) $fileNode->getAttribute('phpVersion');
            } else {
                $phpVersion = PHP_VERSION;
            }

            if ($fileNode->hasAttribute('phpVersionOperator')) {
                $phpVersionOperator = (string) $fileNode->getAttribute('phpVersionOperator');
            } else {
                $phpVersionOperator = '>=';
            }

            if (!version_compare(PHP_VERSION, $phpVersion, $phpVersionOperator)) {
                continue;
            }

            $suite->addTestFile($file);
        }

        return $suite;
    }

    /**
     * @param string $value
     * @param bool   $default
     *
     * @return bool
     */
    protected function getBoolean($value, $default)
    {
        if (strtolower($value) == 'false') {
            return false;
        } elseif (strtolower($value) == 'true') {
            return true;
        }

        return $default;
    }

    /**
     * @param string $value
     * @param bool   $default
     *
     * @return bool
     */
    protected function getInteger($value, $default)
    {
        if (is_numeric($value)) {
            return (int) $value;
        }

        return $default;
    }

    /**
     * @param string $query
     *
     * @return array
     */
    protected function readFilterDirectories($query)
    {
        $directories = [];

        foreach ($this->xpath->query($query) as $directory) {
            $directoryPath = (string) $directory->textContent;

            if (!$directoryPath) {
                continue;
            }

            if ($directory->hasAttribute('prefix')) {
                $prefix = (string) $directory->getAttribute('prefix');
            } else {
                $prefix = '';
            }

            if ($directory->hasAttribute('suffix')) {
                $suffix = (string) $directory->getAttribute('suffix');
            } else {
                $suffix = '.php';
            }

            if ($directory->hasAttribute('group')) {
                $group = (string) $directory->getAttribute('group');
            } else {
                $group = 'DEFAULT';
            }

            $directories[] = [
              'path'   => $this->toAbsolutePath($directoryPath),
              'prefix' => $prefix,
              'suffix' => $suffix,
              'group'  => $group
            ];
        }

        return $directories;
    }

    /**
     * @param string $query
     *
     * @return array
     */
    protected function readFilterFiles($query)
    {
        $files = [];

        foreach ($this->xpath->query($query) as $file) {
            $filePath = (string) $file->textContent;

            if ($filePath) {
                $files[] = $this->toAbsolutePath($filePath);
            }
        }

        return $files;
    }

    /**
     * @param string $path
     * @param bool   $useIncludePath
     *
     * @return string
     */
    protected function toAbsolutePath($path, $useIncludePath = false)
    {
        $path = trim($path);

        if ($path[0] === '/') {
            return $path;
        }

        // Matches the following on Windows:
        //  - \\NetworkComputer\Path
        //  - \\.\D:
        //  - \\.\c:
        //  - C:\Windows
        //  - C:\windows
        //  - C:/windows
        //  - c:/windows
        if (defined('PHP_WINDOWS_VERSION_BUILD') &&
            ($path[0] === '\\' ||
            (strlen($path) >= 3 && preg_match('#^[A-Z]\:[/\\\]#i', substr($path, 0, 3))))) {
            return $path;
        }

        // Stream
        if (strpos($path, '://') !== false) {
            return $path;
        }

        $file = dirname($this->filename) . DIRECTORY_SEPARATOR . $path;

        if ($useIncludePath && !file_exists($file)) {
            $includePathFile = stream_resolve_include_path($path);

            if ($includePathFile) {
                $file = $includePathFile;
            }
        }

        return $file;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Util_ConfigurationGenerator
{
    /**
     * @var string
     */
    private $defaultTemplate = <<<EOT
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/{phpunit_version}/phpunit.xsd"
         bootstrap="{bootstrap_script}"
         backupGlobals="false"
         beStrictAboutCoversAnnotation="true"
         beStrictAboutOutputDuringTests="true"
         beStrictAboutTestsThatDoNotTestAnything="true"
         beStrictAboutTodoAnnotatedTests="true"
         verbose="true">
    <testsuite>
        <directory suffix="Test.php">{tests_directory}</directory>
    </testsuite>

    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">{src_directory}</directory>
        </whitelist>
    </filter>
</phpunit>

EOT;

    /**
     * @param string $phpunitVersion
     * @param string $bootstrapScript
     * @param string $testsDirectory
     * @param string $srcDirectory
     *
     * @return string
     */
    public function generateDefaultConfiguration($phpunitVersion, $bootstrapScript, $testsDirectory, $srcDirectory)
    {
        return str_replace(
            [
                '{phpunit_version}',
                '{bootstrap_script}',
                '{tests_directory}',
                '{src_directory}'
            ],
            [
                $phpunitVersion,
                $bootstrapScript,
                $testsDirectory,
                $srcDirectory
            ],
            $this->defaultTemplate
        );
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

// Workaround for http://bugs.php.net/bug.php?id=47987,
// see https://github.com/sebastianbergmann/phpunit/issues#issue/125 for details
// Use dirname(__DIR__) instead of using /../ because of https://github.com/facebook/hhvm/issues/5215
require_once dirname(__DIR__) . '/Framework/Error.php';
require_once dirname(__DIR__) . '/Framework/Error/Notice.php';
require_once dirname(__DIR__) . '/Framework/Error/Warning.php';
require_once dirname(__DIR__) . '/Framework/Error/Deprecated.php';

/**
 * Error handler that converts PHP errors and warnings to exceptions.
 */
class PHPUnit_Util_ErrorHandler
{
    protected static $errorStack = [];

    /**
     * Returns the error stack.
     *
     * @return array
     */
    public static function getErrorStack()
    {
        return self::$errorStack;
    }

    /**
     * @param int    $errno
     * @param string $errstr
     * @param string $errfile
     * @param int    $errline
     *
     * @throws PHPUnit_Framework_Error
     */
    public static function handleError($errno, $errstr, $errfile, $errline)
    {
        if (!($errno & error_reporting())) {
            return false;
        }

        self::$errorStack[] = [$errno, $errstr, $errfile, $errline];

        $trace = debug_backtrace(false);
        array_shift($trace);

        foreach ($trace as $frame) {
            if ($frame['function'] == '__toString') {
                return false;
            }
        }

        if ($errno == E_NOTICE || $errno == E_USER_NOTICE || $errno == E_STRICT) {
            if (PHPUnit_Framework_Error_Notice::$enabled !== true) {
                return false;
            }

            $exception = 'PHPUnit_Framework_Error_Notice';
        } elseif ($errno == E_WARNING || $errno == E_USER_WARNING) {
            if (PHPUnit_Framework_Error_Warning::$enabled !== true) {
                return false;
            }

            $exception = 'PHPUnit_Framework_Error_Warning';
        } elseif ($errno == E_DEPRECATED || $errno == E_USER_DEPRECATED) {
            if (PHPUnit_Framework_Error_Deprecated::$enabled !== true) {
                return false;
            }

            $exception = 'PHPUnit_Framework_Error_Deprecated';
        } else {
            $exception = 'PHPUnit_Framework_Error';
        }

        throw new $exception($errstr, $errno, $errfile, $errline);
    }

    /**
     * Registers an error handler and returns a function that will restore
     * the previous handler when invoked
     *
     * @param int $severity PHP predefined error constant
     *
     * @throws Exception if event of specified severity is emitted
     */
    public static function handleErrorOnce($severity = E_WARNING)
    {
        $terminator = function () {
            static $expired = false;
            if (!$expired) {
                $expired = true;
                // cleans temporary error handler
                return restore_error_handler();
            }
        };

        set_error_handler(function ($errno, $errstr) use ($severity) {
            if ($errno === $severity) {
                return;
            }

            return false;
        });

        return $terminator;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Utility methods to load PHP sourcefiles.
 */
class PHPUnit_Util_Fileloader
{
    /**
     * Checks if a PHP sourcefile is readable.
     * The sourcefile is loaded through the load() method.
     *
     * @param string $filename
     *
     * @return string
     *
     * @throws PHPUnit_Framework_Exception
     */
    public static function checkAndLoad($filename)
    {
        $includePathFilename = stream_resolve_include_path($filename);

        if (!$includePathFilename || !is_readable($includePathFilename)) {
            throw new PHPUnit_Framework_Exception(
                sprintf('Cannot open file "%s".' . "\n", $filename)
            );
        }

        self::load($includePathFilename);

        return $includePathFilename;
    }

    /**
     * Loads a PHP sourcefile.
     *
     * @param string $filename
     *
     * @return mixed
     */
    public static function load($filename)
    {
        $oldVariableNames = array_keys(get_defined_vars());

        include_once $filename;

        $newVariables     = get_defined_vars();
        $newVariableNames = array_diff(
            array_keys($newVariables),
            $oldVariableNames
        );

        foreach ($newVariableNames as $variableName) {
            if ($variableName != 'oldVariableNames') {
                $GLOBALS[$variableName] = $newVariables[$variableName];
            }
        }

        return $filename;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Filesystem helpers.
 */
class PHPUnit_Util_Filesystem
{
    /**
     * @var array
     */
    protected static $buffer = [];

    /**
     * Maps class names to source file names:
     *   - PEAR CS:   Foo_Bar_Baz -> Foo/Bar/Baz.php
     *   - Namespace: Foo\Bar\Baz -> Foo/Bar/Baz.php
     *
     * @param string $className
     *
     * @return string
     */
    public static function classNameToFilename($className)
    {
        return str_replace(
            ['_', '\\'],
            DIRECTORY_SEPARATOR,
            $className
        ) . '.php';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Utility class for code filtering.
 */
class PHPUnit_Util_Filter
{
    /**
     * Filters stack frames from PHPUnit classes.
     *
     * @param Exception $e
     * @param bool      $asString
     *
     * @return string
     */
    public static function getFilteredStacktrace($e, $asString = true)
    {
        $prefix = false;
        $script = realpath($GLOBALS['_SERVER']['SCRIPT_NAME']);

        if (defined('__PHPUNIT_PHAR_ROOT__')) {
            $prefix = __PHPUNIT_PHAR_ROOT__;
        }

        if ($asString === true) {
            $filteredStacktrace = '';
        } else {
            $filteredStacktrace = [];
        }

        if ($e instanceof PHPUnit_Framework_SyntheticError) {
            $eTrace = $e->getSyntheticTrace();
            $eFile  = $e->getSyntheticFile();
            $eLine  = $e->getSyntheticLine();
        } elseif ($e instanceof PHPUnit_Framework_Exception) {
            $eTrace = $e->getSerializableTrace();
            $eFile  = $e->getFile();
            $eLine  = $e->getLine();
        } else {
            if ($e->getPrevious()) {
                $e = $e->getPrevious();
            }
            $eTrace = $e->getTrace();
            $eFile  = $e->getFile();
            $eLine  = $e->getLine();
        }

        if (!self::frameExists($eTrace, $eFile, $eLine)) {
            array_unshift(
                $eTrace,
                ['file' => $eFile, 'line' => $eLine]
            );
        }

        $blacklist = new PHPUnit_Util_Blacklist;

        foreach ($eTrace as $frame) {
            if (isset($frame['file']) && is_file($frame['file']) &&
                !$blacklist->isBlacklisted($frame['file']) &&
                ($prefix === false || strpos($frame['file'], $prefix) !== 0) &&
                $frame['file'] !== $script) {
                if ($asString === true) {
                    $filteredStacktrace .= sprintf(
                        "%s:%s\n",
                        $frame['file'],
                        isset($frame['line']) ? $frame['line'] : '?'
                    );
                } else {
                    $filteredStacktrace[] = $frame;
                }
            }
        }

        return $filteredStacktrace;
    }

    /**
     * @param array  $trace
     * @param string $file
     * @param int    $line
     *
     * @return bool
     */
    private static function frameExists(array $trace, $file, $line)
    {
        foreach ($trace as $frame) {
            if (isset($frame['file']) && $frame['file'] == $file &&
                isset($frame['line']) && $frame['line'] == $line) {
                return true;
            }
        }

        return false;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Command-line options parsing class.
 */
class PHPUnit_Util_Getopt
{
    public static function getopt(array $args, $short_options, $long_options = null)
    {
        if (empty($args)) {
            return [[], []];
        }

        $opts     = [];
        $non_opts = [];

        if ($long_options) {
            sort($long_options);
        }

        if (isset($args[0][0]) && $args[0][0] != '-') {
            array_shift($args);
        }

        reset($args);

        while (list($i, $arg) = @each($args)) {
            if ($arg == '') {
                continue;
            }

            if ($arg == '--') {
                $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
                break;
            }

            if ($arg[0] != '-' || (strlen($arg) > 1 && $arg[1] == '-' && !$long_options)) {
                $non_opts[] = $args[$i];
                continue;
            } elseif (strlen($arg) > 1 && $arg[1] == '-') {
                self::parseLongOption(
                    substr($arg, 2),
                    $long_options,
                    $opts,
                    $args
                );
            } else {
                self::parseShortOption(
                    substr($arg, 1),
                    $short_options,
                    $opts,
                    $args
                );
            }
        }

        return [$opts, $non_opts];
    }

    protected static function parseShortOption($arg, $short_options, &$opts, &$args)
    {
        $argLen = strlen($arg);

        for ($i = 0; $i < $argLen; $i++) {
            $opt     = $arg[$i];
            $opt_arg = null;

            if (($spec = strstr($short_options, $opt)) === false || $arg[$i] == ':') {
                throw new PHPUnit_Framework_Exception(
                    "unrecognized option -- $opt"
                );
            }

            if (strlen($spec) > 1 && $spec[1] == ':') {
                if (strlen($spec) > 2 && $spec[2] == ':') {
                    if ($i + 1 < $argLen) {
                        $opts[] = [$opt, substr($arg, $i + 1)];
                        break;
                    }
                } else {
                    if ($i + 1 < $argLen) {
                        $opts[] = [$opt, substr($arg, $i + 1)];
                        break;
                    } elseif (list(, $opt_arg) = @each($args)) {
                    } else {
                        throw new PHPUnit_Framework_Exception(
                            "option requires an argument -- $opt"
                        );
                    }
                }
            }

            $opts[] = [$opt, $opt_arg];
        }
    }

    protected static function parseLongOption($arg, $long_options, &$opts, &$args)
    {
        $count   = count($long_options);
        $list    = explode('=', $arg);
        $opt     = $list[0];
        $opt_arg = null;

        if (count($list) > 1) {
            $opt_arg = $list[1];
        }

        $opt_len = strlen($opt);

        for ($i = 0; $i < $count; $i++) {
            $long_opt  = $long_options[$i];
            $opt_start = substr($long_opt, 0, $opt_len);

            if ($opt_start != $opt) {
                continue;
            }

            $opt_rest = substr($long_opt, $opt_len);

            if ($opt_rest != '' && $opt[0] != '=' && $i + 1 < $count &&
                $opt == substr($long_options[$i + 1], 0, $opt_len)) {
                throw new PHPUnit_Framework_Exception(
                    "option --$opt is ambiguous"
                );
            }

            if (substr($long_opt, -1) == '=') {
                if (substr($long_opt, -2) != '==') {
                    if (!strlen($opt_arg) && !(list(, $opt_arg) = @each($args))) {
                        throw new PHPUnit_Framework_Exception(
                            "option --$opt requires an argument"
                        );
                    }
                }
            } elseif ($opt_arg) {
                throw new PHPUnit_Framework_Exception(
                    "option --$opt doesn't allow an argument"
                );
            }

            $full_option = '--' . preg_replace('/={1,2}$/', '', $long_opt);
            $opts[]      = [$full_option, $opt_arg];

            return;
        }

        throw new PHPUnit_Framework_Exception("unrecognized option --$opt");
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Util_GlobalState
{
    /**
     * @var array
     */
    protected static $superGlobalArrays = [
      '_ENV',
      '_POST',
      '_GET',
      '_COOKIE',
      '_SERVER',
      '_FILES',
      '_REQUEST'
    ];

    /**
     * @var array
     */
    protected static $superGlobalArraysLong = [
      'HTTP_ENV_VARS',
      'HTTP_POST_VARS',
      'HTTP_GET_VARS',
      'HTTP_COOKIE_VARS',
      'HTTP_SERVER_VARS',
      'HTTP_POST_FILES'
    ];

    /**
     * @return string
     */
    public static function getIncludedFilesAsString()
    {
        return static::processIncludedFilesAsString(get_included_files());
    }

    /**
     * @param array $files
     *
     * @return string
     */
    public static function processIncludedFilesAsString(array $files)
    {
        $blacklist = new PHPUnit_Util_Blacklist;
        $prefix    = false;
        $result    = '';

        if (defined('__PHPUNIT_PHAR__')) {
            $prefix = 'phar://' . __PHPUNIT_PHAR__ . '/';
        }

        for ($i = count($files) - 1; $i > 0; $i--) {
            $file = $files[$i];

            if ($prefix !== false && strpos($file, $prefix) === 0) {
                continue;
            }

            // Skip virtual file system protocols
            if (preg_match('/^(vfs|phpvfs[a-z0-9]+):/', $file)) {
                continue;
            }

            if (!$blacklist->isBlacklisted($file) && is_file($file)) {
                $result = 'require_once \'' . $file . "';\n" . $result;
            }
        }

        return $result;
    }

    /**
     * @return string
     */
    public static function getIniSettingsAsString()
    {
        $result      = '';
        $iniSettings = ini_get_all(null, false);

        foreach ($iniSettings as $key => $value) {
            $result .= sprintf(
                '@ini_set(%s, %s);' . "\n",
                self::exportVariable($key),
                self::exportVariable($value)
            );
        }

        return $result;
    }

    /**
     * @return string
     */
    public static function getConstantsAsString()
    {
        $constants = get_defined_constants(true);
        $result    = '';

        if (isset($constants['user'])) {
            foreach ($constants['user'] as $name => $value) {
                $result .= sprintf(
                    'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n",
                    $name,
                    $name,
                    self::exportVariable($value)
                );
            }
        }

        return $result;
    }

    /**
     * @return string
     */
    public static function getGlobalsAsString()
    {
        $result            = '';
        $superGlobalArrays = self::getSuperGlobalArrays();

        foreach ($superGlobalArrays as $superGlobalArray) {
            if (isset($GLOBALS[$superGlobalArray]) &&
                is_array($GLOBALS[$superGlobalArray])) {
                foreach (array_keys($GLOBALS[$superGlobalArray]) as $key) {
                    if ($GLOBALS[$superGlobalArray][$key] instanceof Closure) {
                        continue;
                    }

                    $result .= sprintf(
                        '$GLOBALS[\'%s\'][\'%s\'] = %s;' . "\n",
                        $superGlobalArray,
                        $key,
                        self::exportVariable($GLOBALS[$superGlobalArray][$key])
                    );
                }
            }
        }

        $blacklist   = $superGlobalArrays;
        $blacklist[] = 'GLOBALS';

        foreach (array_keys($GLOBALS) as $key) {
            if (!in_array($key, $blacklist) && !$GLOBALS[$key] instanceof Closure) {
                $result .= sprintf(
                    '$GLOBALS[\'%s\'] = %s;' . "\n",
                    $key,
                    self::exportVariable($GLOBALS[$key])
                );
            }
        }

        return $result;
    }

    /**
     * @return array
     */
    protected static function getSuperGlobalArrays()
    {
        if (ini_get('register_long_arrays') == '1') {
            return array_merge(
                self::$superGlobalArrays,
                self::$superGlobalArraysLong
            );
        } else {
            return self::$superGlobalArrays;
        }
    }

    protected static function exportVariable($variable)
    {
        if (is_scalar($variable) || is_null($variable) ||
           (is_array($variable) && self::arrayOnlyContainsScalars($variable))) {
            return var_export($variable, true);
        }

        return 'unserialize(' .
                var_export(serialize($variable), true) .
                ')';
    }

    /**
     * @param array $array
     *
     * @return bool
     */
    protected static function arrayOnlyContainsScalars(array $array)
    {
        $result = true;

        foreach ($array as $element) {
            if (is_array($element)) {
                $result = self::arrayOnlyContainsScalars($element);
            } elseif (!is_scalar($element) && !is_null($element)) {
                $result = false;
            }

            if ($result === false) {
                break;
            }
        }

        return $result;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Factory for PHPUnit_Framework_Exception objects that are used to describe
 * invalid arguments passed to a function or method.
 */
class PHPUnit_Util_InvalidArgumentHelper
{
    /**
     * @param int    $argument
     * @param string $type
     * @param mixed  $value
     *
     * @return PHPUnit_Framework_Exception
     */
    public static function factory($argument, $type, $value = null)
    {
        $stack = debug_backtrace(false);

        return new PHPUnit_Framework_Exception(
            sprintf(
                'Argument #%d%sof %s::%s() must be a %s',
                $argument,
                $value !== null ? ' (' . gettype($value) . '#' . $value . ')' : ' (No Value) ',
                $stack[1]['class'],
                $stack[1]['function'],
                $type
            )
        );
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A TestListener that generates JSON messages.
 */
class PHPUnit_Util_Log_JSON extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
{
    /**
     * @var string
     */
    protected $currentTestSuiteName = '';

    /**
     * @var string
     */
    protected $currentTestName = '';

    /**
     * @var bool
     */
    protected $currentTestPass = true;

    /**
     * An error occurred.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->writeCase(
            'error',
            $time,
            PHPUnit_Util_Filter::getFilteredStacktrace($e, false),
            PHPUnit_Framework_TestFailure::exceptionToString($e),
            $test
        );

        $this->currentTestPass = false;
    }

    /**
     * A warning occurred.
     *
     * @param PHPUnit_Framework_Test    $test
     * @param PHPUnit_Framework_Warning $e
     * @param float                     $time
     */
    public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
    {
        $this->writeCase(
            'warning',
            $time,
            PHPUnit_Util_Filter::getFilteredStacktrace($e, false),
            PHPUnit_Framework_TestFailure::exceptionToString($e),
            $test
        );

        $this->currentTestPass = false;
    }

    /**
     * A failure occurred.
     *
     * @param PHPUnit_Framework_Test                 $test
     * @param PHPUnit_Framework_AssertionFailedError $e
     * @param float                                  $time
     */
    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
    {
        $this->writeCase(
            'fail',
            $time,
            PHPUnit_Util_Filter::getFilteredStacktrace($e, false),
            PHPUnit_Framework_TestFailure::exceptionToString($e),
            $test
        );

        $this->currentTestPass = false;
    }

    /**
     * Incomplete test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->writeCase(
            'error',
            $time,
            PHPUnit_Util_Filter::getFilteredStacktrace($e, false),
            'Incomplete Test: ' . $e->getMessage(),
            $test
        );

        $this->currentTestPass = false;
    }

    /**
     * Risky test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->writeCase(
            'error',
            $time,
            PHPUnit_Util_Filter::getFilteredStacktrace($e, false),
            'Risky Test: ' . $e->getMessage(),
            $test
        );

        $this->currentTestPass = false;
    }

    /**
     * Skipped test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->writeCase(
            'error',
            $time,
            PHPUnit_Util_Filter::getFilteredStacktrace($e, false),
            'Skipped Test: ' . $e->getMessage(),
            $test
        );

        $this->currentTestPass = false;
    }

    /**
     * A testsuite started.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        $this->currentTestSuiteName = $suite->getName();
        $this->currentTestName      = '';

        $this->write(
            [
            'event' => 'suiteStart',
            'suite' => $this->currentTestSuiteName,
            'tests' => count($suite)
            ]
        );
    }

    /**
     * A testsuite ended.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        $this->currentTestSuiteName = '';
        $this->currentTestName      = '';
    }

    /**
     * A test started.
     *
     * @param PHPUnit_Framework_Test $test
     */
    public function startTest(PHPUnit_Framework_Test $test)
    {
        $this->currentTestName = PHPUnit_Util_Test::describe($test);
        $this->currentTestPass = true;

        $this->write(
            [
            'event' => 'testStart',
            'suite' => $this->currentTestSuiteName,
            'test'  => $this->currentTestName
            ]
        );
    }

    /**
     * A test ended.
     *
     * @param PHPUnit_Framework_Test $test
     * @param float                  $time
     */
    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
        if ($this->currentTestPass) {
            $this->writeCase('pass', $time, [], '', $test);
        }
    }

    /**
     * @param string                          $status
     * @param float                           $time
     * @param array                           $trace
     * @param string                          $message
     * @param PHPUnit_Framework_TestCase|null $test
     */
    protected function writeCase($status, $time, array $trace = [], $message = '', $test = null)
    {
        $output = '';
        // take care of TestSuite producing error (e.g. by running into exception) as TestSuite doesn't have hasOutput
        if ($test !== null && method_exists($test, 'hasOutput') && $test->hasOutput()) {
            $output = $test->getActualOutput();
        }
        $this->write(
            [
            'event'   => 'test',
            'suite'   => $this->currentTestSuiteName,
            'test'    => $this->currentTestName,
            'status'  => $status,
            'time'    => $time,
            'trace'   => $trace,
            'message' => PHPUnit_Util_String::convertToUtf8($message),
            'output'  => $output,
            ]
        );
    }

    /**
     * @param string $buffer
     */
    public function write($buffer)
    {
        array_walk_recursive($buffer, function (&$input) {
            if (is_string($input)) {
                $input = PHPUnit_Util_String::convertToUtf8($input);
            }
        });

        parent::write(json_encode($buffer, JSON_PRETTY_PRINT));
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A TestListener that generates a logfile of the test execution in XML markup.
 *
 * The XML markup used is the same as the one that is used by the JUnit Ant task.
 */
class PHPUnit_Util_Log_JUnit extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
{
    /**
     * @var DOMDocument
     */
    protected $document;

    /**
     * @var DOMElement
     */
    protected $root;

    /**
     * @var bool
     */
    protected $logIncompleteSkipped = false;

    /**
     * @var bool
     */
    protected $writeDocument = true;

    /**
     * @var DOMElement[]
     */
    protected $testSuites = [];

    /**
     * @var int[]
     */
    protected $testSuiteTests = [0];

    /**
     * @var int[]
     */
    protected $testSuiteAssertions = [0];

    /**
     * @var int[]
     */
    protected $testSuiteErrors = [0];

    /**
     * @var int[]
     */
    protected $testSuiteFailures = [0];

    /**
     * @var int[]
     */
    protected $testSuiteTimes = [0];

    /**
     * @var int
     */
    protected $testSuiteLevel = 0;

    /**
     * @var DOMElement
     */
    protected $currentTestCase = null;

    /**
     * @var bool
     */
    protected $attachCurrentTestCase = true;

    /**
     * Constructor.
     *
     * @param mixed $out
     * @param bool  $logIncompleteSkipped
     */
    public function __construct($out = null, $logIncompleteSkipped = false)
    {
        $this->document               = new DOMDocument('1.0', 'UTF-8');
        $this->document->formatOutput = true;

        $this->root = $this->document->createElement('testsuites');
        $this->document->appendChild($this->root);

        parent::__construct($out);

        $this->logIncompleteSkipped = $logIncompleteSkipped;
    }

    /**
     * Flush buffer and close output.
     */
    public function flush()
    {
        if ($this->writeDocument === true) {
            $this->write($this->getXML());
        }

        parent::flush();
    }

    /**
     * An error occurred.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->doAddFault($test, $e, $time, 'error');
        $this->testSuiteErrors[$this->testSuiteLevel]++;
    }

    /**
     * A warning occurred.
     *
     * @param PHPUnit_Framework_Test    $test
     * @param PHPUnit_Framework_Warning $e
     * @param float                     $time
     */
    public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
    {
        if (!$this->logIncompleteSkipped) {
            return;
        }

        $this->doAddFault($test, $e, $time, 'warning');
        $this->testSuiteFailures[$this->testSuiteLevel]++;
    }

    /**
     * A failure occurred.
     *
     * @param PHPUnit_Framework_Test                 $test
     * @param PHPUnit_Framework_AssertionFailedError $e
     * @param float                                  $time
     */
    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
    {
        $this->doAddFault($test, $e, $time, 'failure');
        $this->testSuiteFailures[$this->testSuiteLevel]++;
    }

    /**
     * Incomplete test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        if ($this->logIncompleteSkipped && $this->currentTestCase !== null) {
            $error = $this->document->createElement(
                'error',
                PHPUnit_Util_XML::prepareString(
                    "Incomplete Test\n" .
                    PHPUnit_Util_Filter::getFilteredStacktrace($e)
                )
            );

            $error->setAttribute('type', get_class($e));

            $this->currentTestCase->appendChild($error);

            $this->testSuiteErrors[$this->testSuiteLevel]++;
        } else {
            $this->attachCurrentTestCase = false;
        }
    }

    /**
     * Risky test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        if ($this->logIncompleteSkipped && $this->currentTestCase !== null) {
            $error = $this->document->createElement(
                'error',
                PHPUnit_Util_XML::prepareString(
                    "Risky Test\n" .
                    PHPUnit_Util_Filter::getFilteredStacktrace($e)
                )
            );

            $error->setAttribute('type', get_class($e));

            $this->currentTestCase->appendChild($error);

            $this->testSuiteErrors[$this->testSuiteLevel]++;
        } else {
            $this->attachCurrentTestCase = false;
        }
    }

    /**
     * Skipped test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        if ($this->logIncompleteSkipped && $this->currentTestCase !== null) {
            $error = $this->document->createElement(
                'error',
                PHPUnit_Util_XML::prepareString(
                    "Skipped Test\n" .
                    PHPUnit_Util_Filter::getFilteredStacktrace($e)
                )
            );

            $error->setAttribute('type', get_class($e));

            $this->currentTestCase->appendChild($error);

            $this->testSuiteErrors[$this->testSuiteLevel]++;
        } else {
            $this->attachCurrentTestCase = false;
        }
    }

    /**
     * A testsuite started.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        $testSuite = $this->document->createElement('testsuite');
        $testSuite->setAttribute('name', $suite->getName());

        if (class_exists($suite->getName(), false)) {
            try {
                $class = new ReflectionClass($suite->getName());

                $testSuite->setAttribute('file', $class->getFileName());
            } catch (ReflectionException $e) {
            }
        }

        if ($this->testSuiteLevel > 0) {
            $this->testSuites[$this->testSuiteLevel]->appendChild($testSuite);
        } else {
            $this->root->appendChild($testSuite);
        }

        $this->testSuiteLevel++;
        $this->testSuites[$this->testSuiteLevel]          = $testSuite;
        $this->testSuiteTests[$this->testSuiteLevel]      = 0;
        $this->testSuiteAssertions[$this->testSuiteLevel] = 0;
        $this->testSuiteErrors[$this->testSuiteLevel]     = 0;
        $this->testSuiteFailures[$this->testSuiteLevel]   = 0;
        $this->testSuiteTimes[$this->testSuiteLevel]      = 0;
    }

    /**
     * A testsuite ended.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        $this->testSuites[$this->testSuiteLevel]->setAttribute(
            'tests',
            $this->testSuiteTests[$this->testSuiteLevel]
        );

        $this->testSuites[$this->testSuiteLevel]->setAttribute(
            'assertions',
            $this->testSuiteAssertions[$this->testSuiteLevel]
        );

        $this->testSuites[$this->testSuiteLevel]->setAttribute(
            'failures',
            $this->testSuiteFailures[$this->testSuiteLevel]
        );

        $this->testSuites[$this->testSuiteLevel]->setAttribute(
            'errors',
            $this->testSuiteErrors[$this->testSuiteLevel]
        );

        $this->testSuites[$this->testSuiteLevel]->setAttribute(
            'time',
            sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel])
        );

        if ($this->testSuiteLevel > 1) {
            $this->testSuiteTests[$this->testSuiteLevel - 1]      += $this->testSuiteTests[$this->testSuiteLevel];
            $this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel];
            $this->testSuiteErrors[$this->testSuiteLevel - 1]     += $this->testSuiteErrors[$this->testSuiteLevel];
            $this->testSuiteFailures[$this->testSuiteLevel - 1]   += $this->testSuiteFailures[$this->testSuiteLevel];
            $this->testSuiteTimes[$this->testSuiteLevel - 1]      += $this->testSuiteTimes[$this->testSuiteLevel];
        }

        $this->testSuiteLevel--;
    }

    /**
     * A test started.
     *
     * @param PHPUnit_Framework_Test $test
     */
    public function startTest(PHPUnit_Framework_Test $test)
    {
        $testCase = $this->document->createElement('testcase');
        $testCase->setAttribute('name', $test->getName());

        if ($test instanceof PHPUnit_Framework_TestCase) {
            $class      = new ReflectionClass($test);
            $methodName = $test->getName();

            if ($class->hasMethod($methodName)) {
                $method = $class->getMethod($test->getName());

                $testCase->setAttribute('class', $class->getName());
                $testCase->setAttribute('file', $class->getFileName());
                $testCase->setAttribute('line', $method->getStartLine());
            }
        }

        $this->currentTestCase = $testCase;
    }

    /**
     * A test ended.
     *
     * @param PHPUnit_Framework_Test $test
     * @param float                  $time
     */
    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
        if ($this->attachCurrentTestCase) {
            if ($test instanceof PHPUnit_Framework_TestCase) {
                $numAssertions = $test->getNumAssertions();
                $this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions;

                $this->currentTestCase->setAttribute(
                    'assertions',
                    $numAssertions
                );
            }

            $this->currentTestCase->setAttribute(
                'time',
                sprintf('%F', $time)
            );

            $this->testSuites[$this->testSuiteLevel]->appendChild(
                $this->currentTestCase
            );

            $this->testSuiteTests[$this->testSuiteLevel]++;
            $this->testSuiteTimes[$this->testSuiteLevel] += $time;

            if (method_exists($test, 'hasOutput') && $test->hasOutput()) {
                $systemOut = $this->document->createElement('system-out');
                $systemOut->appendChild(
                    $this->document->createTextNode($test->getActualOutput())
                );
                $this->currentTestCase->appendChild($systemOut);
            }
        }

        $this->attachCurrentTestCase = true;
        $this->currentTestCase       = null;
    }

    /**
     * Returns the XML as a string.
     *
     * @return string
     */
    public function getXML()
    {
        return $this->document->saveXML();
    }

    /**
     * Enables or disables the writing of the document
     * in flush().
     *
     * This is a "hack" needed for the integration of
     * PHPUnit with Phing.
     *
     * @return string
     */
    public function setWriteDocument($flag)
    {
        if (is_bool($flag)) {
            $this->writeDocument = $flag;
        }
    }

    /**
     * Method which generalizes addError() and addFailure()
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     * @param string                 $type
     */
    private function doAddFault(PHPUnit_Framework_Test $test, Exception $e, $time, $type)
    {
        if ($this->currentTestCase === null) {
            return;
        }

        if ($test instanceof PHPUnit_Framework_SelfDescribing) {
            $buffer = $test->toString() . PHP_EOL;
        } else {
            $buffer = '';
        }

        $buffer .= PHPUnit_Framework_TestFailure::exceptionToString($e) . PHP_EOL .
                   PHPUnit_Util_Filter::getFilteredStacktrace($e);

        $fault = $this->document->createElement(
            $type,
            PHPUnit_Util_XML::prepareString($buffer)
        );

        if ($e instanceof PHPUnit_Framework_ExceptionWrapper) {
            $fault->setAttribute('type', $e->getClassName());
        } else {
            $fault->setAttribute('type', get_class($e));
        }

        $this->currentTestCase->appendChild($fault);
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * A TestListener that generates a logfile of the
 * test execution using the Test Anything Protocol (TAP).
 */
class PHPUnit_Util_Log_TAP extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
{
    /**
     * @var int
     */
    protected $testNumber = 0;

    /**
     * @var int
     */
    protected $testSuiteLevel = 0;

    /**
     * @var bool
     */
    protected $testSuccessful = true;

    /**
     * Constructor.
     *
     * @param mixed $out
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function __construct($out = null)
    {
        parent::__construct($out);
        $this->write("TAP version 13\n");
    }

    /**
     * An error occurred.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->writeNotOk($test, 'Error');
    }

    /**
     * A warning occurred.
     *
     * @param PHPUnit_Framework_Test    $test
     * @param PHPUnit_Framework_Warning $e
     * @param float                     $time
     */
    public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
    {
        $this->writeNotOk($test, 'Warning');
    }

    /**
     * A failure occurred.
     *
     * @param PHPUnit_Framework_Test                 $test
     * @param PHPUnit_Framework_AssertionFailedError $e
     * @param float                                  $time
     */
    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
    {
        $this->writeNotOk($test, 'Failure');

        $message = explode(
            "\n",
            PHPUnit_Framework_TestFailure::exceptionToString($e)
        );

        $diagnostic = [
          'message'  => $message[0],
          'severity' => 'fail'
        ];

        if ($e instanceof PHPUnit_Framework_ExpectationFailedException) {
            $cf = $e->getComparisonFailure();

            if ($cf !== null) {
                $diagnostic['data'] = [
                  'got'      => $cf->getActual(),
                  'expected' => $cf->getExpected()
                ];
            }
        }

        $yaml = new Symfony\Component\Yaml\Dumper;

        $this->write(
            sprintf(
                "  ---\n%s  ...\n",
                $yaml->dump($diagnostic, 2, 2)
            )
        );
    }

    /**
     * Incomplete test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->writeNotOk($test, '', 'TODO Incomplete Test');
    }

    /**
     * Risky test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->write(
            sprintf(
                "ok %d - # RISKY%s\n",
                $this->testNumber,
                $e->getMessage() != '' ? ' ' . $e->getMessage() : ''
            )
        );

        $this->testSuccessful = false;
    }

    /**
     * Skipped test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->write(
            sprintf(
                "ok %d - # SKIP%s\n",
                $this->testNumber,
                $e->getMessage() != '' ? ' ' . $e->getMessage() : ''
            )
        );

        $this->testSuccessful = false;
    }

    /**
     * A testsuite started.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        $this->testSuiteLevel++;
    }

    /**
     * A testsuite ended.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        $this->testSuiteLevel--;

        if ($this->testSuiteLevel == 0) {
            $this->write(sprintf("1..%d\n", $this->testNumber));
        }
    }

    /**
     * A test started.
     *
     * @param PHPUnit_Framework_Test $test
     */
    public function startTest(PHPUnit_Framework_Test $test)
    {
        $this->testNumber++;
        $this->testSuccessful = true;
    }

    /**
     * A test ended.
     *
     * @param PHPUnit_Framework_Test $test
     * @param float                  $time
     */
    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
        if ($this->testSuccessful === true) {
            $this->write(
                sprintf(
                    "ok %d - %s\n",
                    $this->testNumber,
                    PHPUnit_Util_Test::describe($test)
                )
            );
        }

        $this->writeDiagnostics($test);
    }

    /**
     * @param PHPUnit_Framework_Test $test
     * @param string                 $prefix
     * @param string                 $directive
     */
    protected function writeNotOk(PHPUnit_Framework_Test $test, $prefix = '', $directive = '')
    {
        $this->write(
            sprintf(
                "not ok %d - %s%s%s\n",
                $this->testNumber,
                $prefix != '' ? $prefix . ': ' : '',
                PHPUnit_Util_Test::describe($test),
                $directive != '' ? ' # ' . $directive : ''
            )
        );

        $this->testSuccessful = false;
    }

    /**
     * @param PHPUnit_Framework_Test $test
     */
    private function writeDiagnostics(PHPUnit_Framework_Test $test)
    {
        if (!$test instanceof PHPUnit_Framework_TestCase) {
            return;
        }

        if (!$test->hasOutput()) {
            return;
        }

        foreach (explode("\n", trim($test->getActualOutput())) as $line) {
            $this->write(
                sprintf(
                    "# %s\n",
                    $line
                )
            );
        }
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use SebastianBergmann\Comparator\ComparisonFailure;

/**
 * A TestListener that generates a logfile of the test execution using the
 * TeamCity format (for use with PhpStorm, for instance).
 */
class PHPUnit_Util_Log_TeamCity extends PHPUnit_TextUI_ResultPrinter
{
    /**
     * @var bool
     */
    private $isSummaryTestCountPrinted = false;

    /**
     * @var string
     */
    private $startedTestName;

    /**
     * @var string
     */
    private $flowId;

    /**
     * @param string $progress
     */
    protected function writeProgress($progress)
    {
    }

    /**
     * @param PHPUnit_Framework_TestResult $result
     */
    public function printResult(PHPUnit_Framework_TestResult $result)
    {
        $this->printHeader();
        $this->printFooter($result);
    }

    /**
     * An error occurred.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->printEvent(
            'testFailed',
            [
                'name'    => $test->getName(),
                'message' => self::getMessage($e),
                'details' => self::getDetails($e),
            ]
        );
    }

    /**
     * A warning occurred.
     *
     * @param PHPUnit_Framework_Test    $test
     * @param PHPUnit_Framework_Warning $e
     * @param float                     $time
     */
    public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
    {
        $this->printEvent(
            'testFailed',
            [
                'name'    => $test->getName(),
                'message' => self::getMessage($e),
                'details' => self::getDetails($e)
            ]
        );
    }

    /**
     * A failure occurred.
     *
     * @param PHPUnit_Framework_Test                 $test
     * @param PHPUnit_Framework_AssertionFailedError $e
     * @param float                                  $time
     */
    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
    {
        $parameters = [
            'name'    => $test->getName(),
            'message' => self::getMessage($e),
            'details' => self::getDetails($e),
        ];

        if ($e instanceof PHPUnit_Framework_ExpectationFailedException) {
            $comparisonFailure = $e->getComparisonFailure();

            if ($comparisonFailure instanceof ComparisonFailure) {
                $expectedString = $comparisonFailure->getExpectedAsString();

                if (is_null($expectedString) || empty($expectedString)) {
                    $expectedString = self::getPrimitiveValueAsString($comparisonFailure->getExpected());
                }

                $actualString = $comparisonFailure->getActualAsString();

                if (is_null($actualString) || empty($actualString)) {
                    $actualString = self::getPrimitiveValueAsString($comparisonFailure->getActual());
                }

                if (!is_null($actualString) && !is_null($expectedString)) {
                    $parameters['type']     = 'comparisonFailure';
                    $parameters['actual']   = $actualString;
                    $parameters['expected'] = $expectedString;
                }
            }
        }

        $this->printEvent('testFailed', $parameters);
    }

    /**
     * Incomplete test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->printIgnoredTest($test->getName(), $e);
    }

    /**
     * Risky test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->addError($test, $e, $time);
    }

    /**
     * Skipped test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $testName = $test->getName();
        if ($this->startedTestName != $testName) {
            $this->startTest($test);
            $this->printIgnoredTest($testName, $e);
            $this->endTest($test, $time);
        } else {
            $this->printIgnoredTest($testName, $e);
        }
    }

    public function printIgnoredTest($testName, Exception $e)
    {
        $this->printEvent(
            'testIgnored',
            [
                'name'    => $testName,
                'message' => self::getMessage($e),
                'details' => self::getDetails($e),
            ]
        );
    }

    /**
     * A testsuite started.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        if (stripos(ini_get('disable_functions'), 'getmypid') === false) {
            $this->flowId = getmypid();
        } else {
            $this->flowId = false;
        }

        if (!$this->isSummaryTestCountPrinted) {
            $this->isSummaryTestCountPrinted = true;

            $this->printEvent(
                'testCount',
                ['count' => count($suite)]
            );
        }

        $suiteName = $suite->getName();

        if (empty($suiteName)) {
            return;
        }

        $parameters = ['name' => $suiteName];

        if (class_exists($suiteName, false)) {
            $fileName                   = self::getFileName($suiteName);
            $parameters['locationHint'] = "php_qn://$fileName::\\$suiteName";
        } else {
            $split = preg_split('/::/', $suiteName);

            if (count($split) == 2 && method_exists($split[0], $split[1])) {
                $fileName                   = self::getFileName($split[0]);
                $parameters['locationHint'] = "php_qn://$fileName::\\$suiteName";
                $parameters['name']         = $split[1];
            }
        }

        $this->printEvent('testSuiteStarted', $parameters);
    }

    /**
     * A testsuite ended.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        $suiteName = $suite->getName();

        if (empty($suiteName)) {
            return;
        }

        $parameters = ['name' => $suiteName];

        if (!class_exists($suiteName, false)) {
            $split = preg_split('/::/', $suiteName);

            if (count($split) == 2 && method_exists($split[0], $split[1])) {
                $parameters['name'] = $split[1];
            }
        }

        $this->printEvent('testSuiteFinished', $parameters);
    }

    /**
     * A test started.
     *
     * @param PHPUnit_Framework_Test $test
     */
    public function startTest(PHPUnit_Framework_Test $test)
    {
        $testName              = $test->getName();
        $this->startedTestName = $testName;
        $params                = ['name' => $testName];

        if ($test instanceof PHPUnit_Framework_TestCase) {
            $className              = get_class($test);
            $fileName               = self::getFileName($className);
            $params['locationHint'] = "php_qn://$fileName::\\$className::$testName";
        }

        $this->printEvent('testStarted', $params);
    }

    /**
     * A test ended.
     *
     * @param PHPUnit_Framework_Test $test
     * @param float                  $time
     */
    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
        parent::endTest($test, $time);

        $this->printEvent(
            'testFinished',
            [
                'name'     => $test->getName(),
                'duration' => (int) (round($time, 2) * 1000)
            ]
        );
    }

    /**
     * @param string $eventName
     * @param array  $params
     */
    private function printEvent($eventName, $params = [])
    {
        $this->write("\n##teamcity[$eventName");

        if ($this->flowId) {
            $params['flowId'] = $this->flowId;
        }

        foreach ($params as $key => $value) {
            $escapedValue = self::escapeValue($value);
            $this->write(" $key='$escapedValue'");
        }

        $this->write("]\n");
    }

    /**
     * @param Exception $e
     *
     * @return string
     */
    private static function getMessage(Exception $e)
    {
        $message = '';

        if (!$e instanceof PHPUnit_Framework_Exception) {
            if (strlen(get_class($e)) != 0) {
                $message = $message . get_class($e);
            }

            if (strlen($message) != 0 && strlen($e->getMessage()) != 0) {
                $message = $message . ' : ';
            }
        }

        return $message . $e->getMessage();
    }

    /**
     * @param Exception $e
     *
     * @return string
     */
    private static function getDetails(Exception $e)
    {
        $stackTrace = PHPUnit_Util_Filter::getFilteredStacktrace($e);
        $previous   = $e->getPrevious();

        while ($previous) {
            $stackTrace .= "\nCaused by\n" .
                PHPUnit_Framework_TestFailure::exceptionToString($previous) . "\n" .
                PHPUnit_Util_Filter::getFilteredStacktrace($previous);

            $previous = $previous->getPrevious();
        }

        return ' ' . str_replace("\n", "\n ", $stackTrace);
    }

    /**
     * @param mixed $value
     *
     * @return string
     */
    private static function getPrimitiveValueAsString($value)
    {
        if (is_null($value)) {
            return 'null';
        } elseif (is_bool($value)) {
            return $value == true ? 'true' : 'false';
        } elseif (is_scalar($value)) {
            return print_r($value, true);
        }

        return;
    }

    /**
     * @param  $text
     *
     * @return string
     */
    private static function escapeValue($text)
    {
        $text = str_replace('|', '||', $text);
        $text = str_replace("'", "|'", $text);
        $text = str_replace("\n", '|n', $text);
        $text = str_replace("\r", '|r', $text);
        $text = str_replace(']', '|]', $text);
        $text = str_replace('[', '|[', $text);

        return $text;
    }

    /**
     * @param string $className
     *
     * @return string
     */
    private static function getFileName($className)
    {
        $reflectionClass = new ReflectionClass($className);
        $fileName        = $reflectionClass->getFileName();

        return $fileName;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Default utility for PHP sub-processes.
 */
class PHPUnit_Util_PHP_Default extends PHPUnit_Util_PHP
{
    /**
     * @var string
     */
    protected $tempFile;

    /**
     * @var bool
     */
    protected $useTempFile = false;

    /**
     * Runs a single job (PHP code) using a separate PHP process.
     *
     * @param string $job
     * @param array  $settings
     *
     * @return array
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function runJob($job, array $settings = [])
    {
        if ($this->useTempFile || $this->stdin) {
            if (!($this->tempFile = tempnam(sys_get_temp_dir(), 'PHPUnit')) ||
                file_put_contents($this->tempFile, $job) === false) {
                throw new PHPUnit_Framework_Exception(
                    'Unable to write temporary file'
                );
            }

            $job = $this->stdin;
        }

        return $this->runProcess($job, $settings);
    }

    /**
     * Returns an array of file handles to be used in place of pipes
     *
     * @return array
     */
    protected function getHandles()
    {
        return [];
    }

    /**
     * Handles creating the child process and returning the STDOUT and STDERR
     *
     * @param string $job
     * @param array  $settings
     *
     * @return array
     *
     * @throws PHPUnit_Framework_Exception
     */
    protected function runProcess($job, $settings)
    {
        $handles = $this->getHandles();

        $env = null;
        if ($this->env) {
            $env = isset($_SERVER) ? $_SERVER : [];
            unset($env['argv'], $env['argc']);
            $env = array_merge($env, $this->env);

            foreach ($env as $envKey => $envVar) {
                if (is_array($envVar)) {
                    unset($env[$envKey]);
                }
            }
        }

        $pipeSpec = [
            0 => isset($handles[0]) ? $handles[0] : ['pipe', 'r'],
            1 => isset($handles[1]) ? $handles[1] : ['pipe', 'w'],
            2 => isset($handles[2]) ? $handles[2] : ['pipe', 'w'],
        ];
        $process = proc_open(
            $this->getCommand($settings, $this->tempFile),
            $pipeSpec,
            $pipes,
            null,
            $env
        );

        if (!is_resource($process)) {
            throw new PHPUnit_Framework_Exception(
                'Unable to spawn worker process'
            );
        }

        if ($job) {
            $this->process($pipes[0], $job);
        }
        fclose($pipes[0]);

        if ($this->timeout) {
            $stderr = $stdout = '';
            unset($pipes[0]);

            while (true) {
                $r = $pipes;
                $w = null;
                $e = null;

                $n = @stream_select($r, $w, $e, $this->timeout);

                if ($n === false) {
                    break;
                } elseif ($n === 0) {
                    proc_terminate($process, 9);
                    throw new PHPUnit_Framework_Exception(sprintf('Job execution aborted after %d seconds', $this->timeout));
                } elseif ($n > 0) {
                    foreach ($r as $pipe) {
                        $pipeOffset = 0;
                        foreach ($pipes as $i => $origPipe) {
                            if ($pipe == $origPipe) {
                                $pipeOffset = $i;
                                break;
                            }
                        }

                        if (!$pipeOffset) {
                            break;
                        }

                        $line = fread($pipe, 8192);
                        if (strlen($line) == 0) {
                            fclose($pipes[$pipeOffset]);
                            unset($pipes[$pipeOffset]);
                        } else {
                            if ($pipeOffset == 1) {
                                $stdout .= $line;
                            } else {
                                $stderr .= $line;
                            }
                        }
                    }

                    if (empty($pipes)) {
                        break;
                    }
                }
            }
        } else {
            if (isset($pipes[1])) {
                $stdout = stream_get_contents($pipes[1]);
                fclose($pipes[1]);
            }

            if (isset($pipes[2])) {
                $stderr = stream_get_contents($pipes[2]);
                fclose($pipes[2]);
            }
        }

        if (isset($handles[1])) {
            rewind($handles[1]);
            $stdout = stream_get_contents($handles[1]);
            fclose($handles[1]);
        }

        if (isset($handles[2])) {
            rewind($handles[2]);
            $stderr = stream_get_contents($handles[2]);
            fclose($handles[2]);
        }

        proc_close($process);
        $this->cleanup();

        return ['stdout' => $stdout, 'stderr' => $stderr];
    }

    /**
     * @param resource $pipe
     * @param string   $job
     *
     * @throws PHPUnit_Framework_Exception
     */
    protected function process($pipe, $job)
    {
        fwrite($pipe, $job);
    }

    protected function cleanup()
    {
        if ($this->tempFile) {
            unlink($this->tempFile);
        }
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
eval('?>' . file_get_contents('php://stdin'));
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Windows utility for PHP sub-processes.
 *
 * Reading from STDOUT or STDERR hangs forever on Windows if the output is
 * too large.
 *
 * @see https://bugs.php.net/bug.php?id=51800
 */
class PHPUnit_Util_PHP_Windows extends PHPUnit_Util_PHP_Default
{
    protected $useTempFile = true;

    protected function getHandles()
    {
        if (false === $stdout_handle = tmpfile()) {
            throw new PHPUnit_Framework_Exception(
                'A temporary file could not be created; verify that your TEMP environment variable is writable'
            );
        }

        return [
            1 => $stdout_handle
        ];
    }

    public function getCommand(array $settings, $file = null)
    {
        return '"' . parent::getCommand($settings, $file) . '"';
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use SebastianBergmann\Environment\Runtime;

/**
 * Utility methods for PHP sub-processes.
 */
abstract class PHPUnit_Util_PHP
{
    /**
     * @var Runtime
     */
    protected $runtime;

    /**
     * @var bool
     */
    protected $stderrRedirection = false;

    /**
     * @var string
     */
    protected $stdin = '';

    /**
     * @var string
     */
    protected $args = '';

    /**
     * @var array
     */
    protected $env = [];

    /**
     * @var int
     */
    protected $timeout = 0;

    /**
     * Creates internal Runtime instance.
     */
    public function __construct()
    {
        $this->runtime = new Runtime();
    }

    /**
     * Defines if should use STDERR redirection or not.
     *
     * Then $stderrRedirection is TRUE, STDERR is redirected to STDOUT.
     *
     * @throws PHPUnit_Framework_Exception
     *
     * @param bool $stderrRedirection
     */
    public function setUseStderrRedirection($stderrRedirection)
    {
        if (!is_bool($stderrRedirection)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }

        $this->stderrRedirection = $stderrRedirection;
    }

    /**
     * Returns TRUE if uses STDERR redirection or FALSE if not.
     *
     * @return bool
     */
    public function useStderrRedirection()
    {
        return $this->stderrRedirection;
    }

    /**
     * Sets the input string to be sent via STDIN
     *
     * @param string $stdin
     */
    public function setStdin($stdin)
    {
        $this->stdin = (string) $stdin;
    }

    /**
     * Returns the input string to be sent via STDIN
     *
     * @return string
     */
    public function getStdin()
    {
        return $this->stdin;
    }

    /**
     * Sets the string of arguments to pass to the php job
     *
     * @param string $args
     */
    public function setArgs($args)
    {
        $this->args = (string) $args;
    }

    /**
     * Returns the string of arguments to pass to the php job
     *
     * @retrun string
     */
    public function getArgs()
    {
        return $this->args;
    }

    /**
     * Sets the array of environment variables to start the child process with
     *
     * @param array $env
     */
    public function setEnv(array $env)
    {
        $this->env = $env;
    }

    /**
     * Returns the array of environment variables to start the child process with
     *
     * @return array
     */
    public function getEnv()
    {
        return $this->env;
    }

    /**
     * Sets the amount of seconds to wait before timing out
     *
     * @param int $timeout
     */
    public function setTimeout($timeout)
    {
        $this->timeout = (int) $timeout;
    }

    /**
     * Returns the amount of seconds to wait before timing out
     *
     * @return int
     */
    public function getTimeout()
    {
        return $this->timeout;
    }

    /**
     * @return PHPUnit_Util_PHP
     */
    public static function factory()
    {
        if (DIRECTORY_SEPARATOR == '\\') {
            return new PHPUnit_Util_PHP_Windows;
        }

        return new PHPUnit_Util_PHP_Default;
    }

    /**
     * Runs a single test in a separate PHP process.
     *
     * @param string                       $job
     * @param PHPUnit_Framework_Test       $test
     * @param PHPUnit_Framework_TestResult $result
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function runTestJob($job, PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result)
    {
        $result->startTest($test);

        $_result = $this->runJob($job);

        $this->processChildResult(
            $test,
            $result,
            $_result['stdout'],
            $_result['stderr']
        );
    }

    /**
     * Returns the command based into the configurations.
     *
     * @param array       $settings
     * @param string|null $file
     *
     * @return string
     */
    public function getCommand(array $settings, $file = null)
    {
        $command = $this->runtime->getBinary();
        $command .= $this->settingsToParameters($settings);

        if ('phpdbg' === PHP_SAPI) {
            $command .= ' -qrr ';

            if ($file) {
                $command .= '-e ' . escapeshellarg($file);
            } else {
                $command .= escapeshellarg(__DIR__ . '/PHP/eval-stdin.php');
            }
        } elseif ($file) {
            $command .= ' -f ' . escapeshellarg($file);
        }

        if ($this->args) {
            $command .= ' -- ' . $this->args;
        }

        if (true === $this->stderrRedirection) {
            $command .= ' 2>&1';
        }

        return $command;
    }

    /**
     * Runs a single job (PHP code) using a separate PHP process.
     *
     * @param string $job
     * @param array  $settings
     *
     * @return array
     *
     * @throws PHPUnit_Framework_Exception
     */
    abstract public function runJob($job, array $settings = []);

    /**
     * @param array $settings
     *
     * @return string
     */
    protected function settingsToParameters(array $settings)
    {
        $buffer = '';

        foreach ($settings as $setting) {
            $buffer .= ' -d ' . $setting;
        }

        return $buffer;
    }

    /**
     * Processes the TestResult object from an isolated process.
     *
     * @param PHPUnit_Framework_Test       $test
     * @param PHPUnit_Framework_TestResult $result
     * @param string                       $stdout
     * @param string                       $stderr
     */
    private function processChildResult(PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result, $stdout, $stderr)
    {
        $time = 0;

        if (!empty($stderr)) {
            $result->addError(
                $test,
                new PHPUnit_Framework_Exception(trim($stderr)),
                $time
            );
        } else {
            set_error_handler(function ($errno, $errstr, $errfile, $errline) {
                throw new ErrorException($errstr, $errno, $errno, $errfile, $errline);
            });
            try {
                if (strpos($stdout, "#!/usr/bin/env php\n") === 0) {
                    $stdout = substr($stdout, 19);
                }

                $childResult = unserialize(str_replace("#!/usr/bin/env php\n", '', $stdout));
                restore_error_handler();
            } catch (ErrorException $e) {
                restore_error_handler();
                $childResult = false;

                $result->addError(
                    $test,
                    new PHPUnit_Framework_Exception(trim($stdout), 0, $e),
                    $time
                );
            }

            if ($childResult !== false) {
                if (!empty($childResult['output'])) {
                    $output = $childResult['output'];
                }

                $test->setResult($childResult['testResult']);
                $test->addToAssertionCount($childResult['numAssertions']);

                $childResult = $childResult['result'];
                /* @var $childResult PHPUnit_Framework_TestResult */

                if ($result->getCollectCodeCoverageInformation()) {
                    $result->getCodeCoverage()->merge(
                        $childResult->getCodeCoverage()
                    );
                }

                $time           = $childResult->time();
                $notImplemented = $childResult->notImplemented();
                $risky          = $childResult->risky();
                $skipped        = $childResult->skipped();
                $errors         = $childResult->errors();
                $warnings       = $childResult->warnings();
                $failures       = $childResult->failures();

                if (!empty($notImplemented)) {
                    $result->addError(
                        $test,
                        $this->getException($notImplemented[0]),
                        $time
                    );
                } elseif (!empty($risky)) {
                    $result->addError(
                        $test,
                        $this->getException($risky[0]),
                        $time
                    );
                } elseif (!empty($skipped)) {
                    $result->addError(
                        $test,
                        $this->getException($skipped[0]),
                        $time
                    );
                } elseif (!empty($errors)) {
                    $result->addError(
                        $test,
                        $this->getException($errors[0]),
                        $time
                    );
                } elseif (!empty($warnings)) {
                    $result->addWarning(
                        $test,
                        $this->getException($warnings[0]),
                        $time
                    );
                } elseif (!empty($failures)) {
                    $result->addFailure(
                        $test,
                        $this->getException($failures[0]),
                        $time
                    );
                }
            }
        }

        $result->endTest($test, $time);

        if (!empty($output)) {
            print $output;
        }
    }

    /**
     * Gets the thrown exception from a PHPUnit_Framework_TestFailure.
     *
     * @param PHPUnit_Framework_TestFailure $error
     *
     * @return Exception
     *
     * @see    https://github.com/sebastianbergmann/phpunit/issues/74
     */
    private function getException(PHPUnit_Framework_TestFailure $error)
    {
        $exception = $error->thrownException();

        if ($exception instanceof __PHP_Incomplete_Class) {
            $exceptionArray = [];
            foreach ((array) $exception as $key => $value) {
                $key                  = substr($key, strrpos($key, "\0") + 1);
                $exceptionArray[$key] = $value;
            }

            $exception = new PHPUnit_Framework_SyntheticError(
                sprintf(
                    '%s: %s',
                    $exceptionArray['_PHP_Incomplete_Class_Name'],
                    $exceptionArray['message']
                ),
                $exceptionArray['code'],
                $exceptionArray['file'],
                $exceptionArray['line'],
                $exceptionArray['trace']
            );
        }

        return $exception;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Utility class that can print to STDOUT or write to a file.
 */
class PHPUnit_Util_Printer
{
    /**
     * If true, flush output after every write.
     *
     * @var bool
     */
    protected $autoFlush = false;

    /**
     * @var resource
     */
    protected $out;

    /**
     * @var string
     */
    protected $outTarget;

    /**
     * Constructor.
     *
     * @param mixed $out
     *
     * @throws PHPUnit_Framework_Exception
     */
    public function __construct($out = null)
    {
        if ($out !== null) {
            if (is_string($out)) {
                if (strpos($out, 'socket://') === 0) {
                    $out = explode(':', str_replace('socket://', '', $out));

                    if (count($out) != 2) {
                        throw new PHPUnit_Framework_Exception;
                    }

                    $this->out = fsockopen($out[0], $out[1]);
                } else {
                    if (strpos($out, 'php://') === false &&
                        !is_dir(dirname($out))) {
                        mkdir(dirname($out), 0777, true);
                    }

                    $this->out = fopen($out, 'wt');
                }

                $this->outTarget = $out;
            } else {
                $this->out = $out;
            }
        }
    }

    /**
     * Flush buffer and close output if it's not to a PHP stream
     */
    public function flush()
    {
        if ($this->out && strncmp($this->outTarget, 'php://', 6) !== 0) {
            fclose($this->out);
        }
    }

    /**
     * Performs a safe, incremental flush.
     *
     * Do not confuse this function with the flush() function of this class,
     * since the flush() function may close the file being written to, rendering
     * the current object no longer usable.
     */
    public function incrementalFlush()
    {
        if ($this->out) {
            fflush($this->out);
        } else {
            flush();
        }
    }

    /**
     * @param string $buffer
     */
    public function write($buffer)
    {
        if ($this->out) {
            fwrite($this->out, $buffer);

            if ($this->autoFlush) {
                $this->incrementalFlush();
            }
        } else {
            if (PHP_SAPI != 'cli' && PHP_SAPI != 'phpdbg') {
                $buffer = htmlspecialchars($buffer, ENT_SUBSTITUTE);
            }

            print $buffer;

            if ($this->autoFlush) {
                $this->incrementalFlush();
            }
        }
    }

    /**
     * Check auto-flush mode.
     *
     * @return bool
     */
    public function getAutoFlush()
    {
        return $this->autoFlush;
    }

    /**
     * Set auto-flushing mode.
     *
     * If set, *incremental* flushes will be done after each write. This should
     * not be confused with the different effects of this class' flush() method.
     *
     * @param bool $autoFlush
     */
    public function setAutoFlush($autoFlush)
    {
        if (is_bool($autoFlush)) {
            $this->autoFlush = $autoFlush;
        } else {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
        }
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Error handler that converts PHP errors and warnings to exceptions.
 */
class PHPUnit_Util_Regex
{
    /**
     * @param string $pattern
     * @param string $subject
     * @param null   $matches
     * @param int    $flags
     * @param int    $offset
     *
     * @return int
     */
    public static function pregMatchSafe($pattern, $subject, $matches = null, $flags = 0, $offset = 0)
    {
        $handler_terminator = PHPUnit_Util_ErrorHandler::handleErrorOnce(E_WARNING);
        $match              = preg_match($pattern, $subject, $matches, $flags, $offset);
        $handler_terminator(); // cleaning

        return $match;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * String helpers.
 */
class PHPUnit_Util_String
{
    /**
     * Converts a string to UTF-8 encoding.
     *
     * @param string $string
     *
     * @return string
     */
    public static function convertToUtf8($string)
    {
        return mb_convert_encoding($string, 'UTF-8');
    }

    /**
     * Checks a string for UTF-8 encoding.
     *
     * @param string $string
     *
     * @return bool
     */
    protected static function isUtf8($string)
    {
        $length = strlen($string);

        for ($i = 0; $i < $length; $i++) {
            if (ord($string[$i]) < 0x80) {
                $n = 0;
            } elseif ((ord($string[$i]) & 0xE0) == 0xC0) {
                $n = 1;
            } elseif ((ord($string[$i]) & 0xF0) == 0xE0) {
                $n = 2;
            } elseif ((ord($string[$i]) & 0xF0) == 0xF0) {
                $n = 3;
            } else {
                return false;
            }

            for ($j = 0; $j < $n; $j++) {
                if ((++$i == $length) || ((ord($string[$i]) & 0xC0) != 0x80)) {
                    return false;
                }
            }
        }

        return true;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Test helpers.
 */
class PHPUnit_Util_Test
{
    const REGEX_DATA_PROVIDER      = '/@dataProvider\s+([a-zA-Z0-9._:-\\\\x7f-\xff]+)/';
    const REGEX_TEST_WITH          = '/@testWith\s+/';
    const REGEX_EXPECTED_EXCEPTION = '(@expectedException\s+([:.\w\\\\x7f-\xff]+)(?:[\t ]+(\S*))?(?:[\t ]+(\S*))?\s*$)m';
    const REGEX_REQUIRES_VERSION   = '/@requires\s+(?P<name>PHP(?:Unit)?)\s+(?P<operator>[<>=!]{0,2})\s*(?P<version>[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m';
    const REGEX_REQUIRES_OS        = '/@requires\s+OS\s+(?P<value>.+?)[ \t]*\r?$/m';
    const REGEX_REQUIRES           = '/@requires\s+(?P<name>function|extension)\s+(?P<value>([^ ]+?))\s*(?P<operator>[<>=!]{0,2})\s*(?P<version>[\d\.-]+[\d\.]?)?[ \t]*\r?$/m';

    const UNKNOWN = -1;
    const SMALL   = 0;
    const MEDIUM  = 1;
    const LARGE   = 2;

    private static $annotationCache = [];

    private static $hookMethods = [];

    /**
     * @param PHPUnit_Framework_Test $test
     * @param bool                   $asString
     *
     * @return mixed
     */
    public static function describe(PHPUnit_Framework_Test $test, $asString = true)
    {
        if ($asString) {
            if ($test instanceof PHPUnit_Framework_SelfDescribing) {
                return $test->toString();
            } else {
                return get_class($test);
            }
        } else {
            if ($test instanceof PHPUnit_Framework_TestCase) {
                return [
                  get_class($test), $test->getName()
                ];
            } elseif ($test instanceof PHPUnit_Framework_SelfDescribing) {
                return ['', $test->toString()];
            } else {
                return ['', get_class($test)];
            }
        }
    }

    /**
     * @param string $className
     * @param string $methodName
     *
     * @return array|bool
     *
     * @throws PHPUnit_Framework_CodeCoverageException
     */
    public static function getLinesToBeCovered($className, $methodName)
    {
        $annotations = self::parseTestMethodAnnotations(
            $className,
            $methodName
        );

        if (isset($annotations['class']['coversNothing']) || isset($annotations['method']['coversNothing'])) {
            return false;
        }

        return self::getLinesToBeCoveredOrUsed($className, $methodName, 'covers');
    }

    /**
     * Returns lines of code specified with the @uses annotation.
     *
     * @param string $className
     * @param string $methodName
     *
     * @return array
     */
    public static function getLinesToBeUsed($className, $methodName)
    {
        return self::getLinesToBeCoveredOrUsed($className, $methodName, 'uses');
    }

    /**
     * @param string $className
     * @param string $methodName
     * @param string $mode
     *
     * @return array
     *
     * @throws PHPUnit_Framework_CodeCoverageException
     */
    private static function getLinesToBeCoveredOrUsed($className, $methodName, $mode)
    {
        $annotations = self::parseTestMethodAnnotations(
            $className,
            $methodName
        );

        $classShortcut = null;

        if (!empty($annotations['class'][$mode . 'DefaultClass'])) {
            if (count($annotations['class'][$mode . 'DefaultClass']) > 1) {
                throw new PHPUnit_Framework_CodeCoverageException(
                    sprintf(
                        'More than one @%sClass annotation in class or interface "%s".',
                        $mode,
                        $className
                    )
                );
            }

            $classShortcut = $annotations['class'][$mode . 'DefaultClass'][0];
        }

        $list = [];

        if (isset($annotations['class'][$mode])) {
            $list = $annotations['class'][$mode];
        }

        if (isset($annotations['method'][$mode])) {
            $list = array_merge($list, $annotations['method'][$mode]);
        }

        $codeList = [];

        foreach (array_unique($list) as $element) {
            if ($classShortcut && strncmp($element, '::', 2) === 0) {
                $element = $classShortcut . $element;
            }

            $element = preg_replace('/[\s()]+$/', '', $element);
            $element = explode(' ', $element);
            $element = $element[0];

            $codeList = array_merge(
                $codeList,
                self::resolveElementToReflectionObjects($element)
            );
        }

        return self::resolveReflectionObjectsToLines($codeList);
    }

    /**
     * Returns the requirements for a test.
     *
     * @param string $className
     * @param string $methodName
     *
     * @return array
     */
    public static function getRequirements($className, $methodName)
    {
        $reflector  = new ReflectionClass($className);
        $docComment = $reflector->getDocComment();
        $reflector  = new ReflectionMethod($className, $methodName);
        $docComment .= "\n" . $reflector->getDocComment();
        $requires   = [];

        if ($count = preg_match_all(self::REGEX_REQUIRES_OS, $docComment, $matches)) {
            $requires['OS'] = sprintf(
                '/%s/i',
                addcslashes($matches['value'][$count - 1], '/')
            );
        }
        if ($count = preg_match_all(self::REGEX_REQUIRES_VERSION, $docComment, $matches)) {
            for ($i = 0; $i < $count; $i++) {
                $requires[$matches['name'][$i]] = [
                    'version'  => $matches['version'][$i],
                    'operator' => $matches['operator'][$i]
                ];
            }
        }

        // https://bugs.php.net/bug.php?id=63055
        $matches = [];

        if ($count = preg_match_all(self::REGEX_REQUIRES, $docComment, $matches)) {
            for ($i = 0; $i < $count; $i++) {
                $name = $matches['name'][$i] . 's';
                if (!isset($requires[$name])) {
                    $requires[$name] = [];
                }
                $requires[$name][] = $matches['value'][$i];
                if (empty($matches['version'][$i]) || $name != 'extensions') {
                    continue;
                }
                $requires['extension_versions'][$matches['value'][$i]] = [
                    'version'  => $matches['version'][$i],
                    'operator' => $matches['operator'][$i]
                ];
            }
        }

        return $requires;
    }

    /**
     * Returns the missing requirements for a test.
     *
     * @param string $className
     * @param string $methodName
     *
     * @return array
     */
    public static function getMissingRequirements($className, $methodName)
    {
        $required = static::getRequirements($className, $methodName);
        $missing  = [];

        $operator = empty($required['PHP']['operator']) ? '>=' : $required['PHP']['operator'];
        if (!empty($required['PHP']) && !version_compare(PHP_VERSION, $required['PHP']['version'], $operator)) {
            $missing[] = sprintf('PHP %s %s is required.', $operator, $required['PHP']['version']);
        }

        if (!empty($required['PHPUnit'])) {
            $phpunitVersion = PHPUnit_Runner_Version::id();

            $operator = empty($required['PHPUnit']['operator']) ? '>=' : $required['PHPUnit']['operator'];
            if (!version_compare($phpunitVersion, $required['PHPUnit']['version'], $operator)) {
                $missing[] = sprintf('PHPUnit %s %s is required.', $operator, $required['PHPUnit']['version']);
            }
        }

        if (!empty($required['OS']) && !preg_match($required['OS'], PHP_OS)) {
            $missing[] = sprintf('Operating system matching %s is required.', $required['OS']);
        }

        if (!empty($required['functions'])) {
            foreach ($required['functions'] as $function) {
                $pieces = explode('::', $function);
                if (2 === count($pieces) && method_exists($pieces[0], $pieces[1])) {
                    continue;
                }
                if (function_exists($function)) {
                    continue;
                }
                $missing[] = sprintf('Function %s is required.', $function);
            }
        }

        if (!empty($required['extensions'])) {
            foreach ($required['extensions'] as $extension) {
                if (isset($required['extension_versions'][$extension])) {
                    continue;
                }
                if (!extension_loaded($extension)) {
                    $missing[] = sprintf('Extension %s is required.', $extension);
                }
            }
        }

        if (!empty($required['extension_versions'])) {
            foreach ($required['extension_versions'] as $extension => $required) {
                $actualVersion = phpversion($extension);

                $operator = empty($required['operator']) ? '>=' : $required['operator'];
                if (false === $actualVersion || !version_compare($actualVersion, $required['version'], $operator)) {
                    $missing[] = sprintf('Extension %s %s %s is required.', $extension, $operator, $required['version']);
                }
            }
        }

        return $missing;
    }

    /**
     * Returns the expected exception for a test.
     *
     * @param string $className
     * @param string $methodName
     *
     * @return array
     */
    public static function getExpectedException($className, $methodName)
    {
        $reflector  = new ReflectionMethod($className, $methodName);
        $docComment = $reflector->getDocComment();
        $docComment = substr($docComment, 3, -2);

        if (preg_match(self::REGEX_EXPECTED_EXCEPTION, $docComment, $matches)) {
            $annotations = self::parseTestMethodAnnotations(
                $className,
                $methodName
            );

            $class         = $matches[1];
            $code          = null;
            $message       = '';
            $messageRegExp = '';

            if (isset($matches[2])) {
                $message = trim($matches[2]);
            } elseif (isset($annotations['method']['expectedExceptionMessage'])) {
                $message = self::parseAnnotationContent(
                    $annotations['method']['expectedExceptionMessage'][0]
                );
            }

            if (isset($annotations['method']['expectedExceptionMessageRegExp'])) {
                $messageRegExp = self::parseAnnotationContent(
                    $annotations['method']['expectedExceptionMessageRegExp'][0]
                );
            }

            if (isset($matches[3])) {
                $code = $matches[3];
            } elseif (isset($annotations['method']['expectedExceptionCode'])) {
                $code = self::parseAnnotationContent(
                    $annotations['method']['expectedExceptionCode'][0]
                );
            }

            if (is_numeric($code)) {
                $code = (int) $code;
            } elseif (is_string($code) && defined($code)) {
                $code = (int) constant($code);
            }

            return [
              'class' => $class, 'code' => $code, 'message' => $message, 'message_regex' => $messageRegExp
            ];
        }

        return false;
    }

    /**
     * Parse annotation content to use constant/class constant values
     *
     * Constants are specified using a starting '@'. For example: @ClassName::CONST_NAME
     *
     * If the constant is not found the string is used as is to ensure maximum BC.
     *
     * @param string $message
     *
     * @return string
     */
    private static function parseAnnotationContent($message)
    {
        if (strpos($message, '::') !== false && count(explode('::', $message)) == 2) {
            if (defined($message)) {
                $message = constant($message);
            }
        }

        return $message;
    }

    /**
     * Returns the provided data for a method.
     *
     * @param string $className
     * @param string $methodName
     *
     * @return array When a data provider is specified and exists
     *         null  When no data provider is specified
     *
     * @throws PHPUnit_Framework_Exception
     */
    public static function getProvidedData($className, $methodName)
    {
        $reflector  = new ReflectionMethod($className, $methodName);
        $docComment = $reflector->getDocComment();

        $data = self::getDataFromDataProviderAnnotation($docComment, $className, $methodName);

        if ($data === null) {
            $data = self::getDataFromTestWithAnnotation($docComment);
        }

        if (is_array($data) && empty($data)) {
            throw new PHPUnit_Framework_SkippedTestError;
        }

        if ($data !== null) {
            foreach ($data as $key => $value) {
                if (!is_array($value)) {
                    throw new PHPUnit_Framework_Exception(
                        sprintf(
                            'Data set %s is invalid.',
                            is_int($key) ? '#' . $key : '"' . $key . '"'
                        )
                    );
                }
            }
        }

        return $data;
    }

    /**
     * Returns the provided data for a method.
     *
     * @param string $docComment
     * @param string $className
     * @param string $methodName
     *
     * @return array|Iterator when a data provider is specified and exists
     *                        null           when no data provider is specified
     *
     * @throws PHPUnit_Framework_Exception
     */
    private static function getDataFromDataProviderAnnotation($docComment, $className, $methodName)
    {
        if (preg_match_all(self::REGEX_DATA_PROVIDER, $docComment, $matches)) {
            $result = [];

            foreach ($matches[1] as $match) {
                $dataProviderMethodNameNamespace = explode('\\', $match);
                $leaf                            = explode('::', array_pop($dataProviderMethodNameNamespace));
                $dataProviderMethodName          = array_pop($leaf);

                if (!empty($dataProviderMethodNameNamespace)) {
                    $dataProviderMethodNameNamespace = implode('\\', $dataProviderMethodNameNamespace) . '\\';
                } else {
                    $dataProviderMethodNameNamespace = '';
                }

                if (!empty($leaf)) {
                    $dataProviderClassName = $dataProviderMethodNameNamespace . array_pop($leaf);
                } else {
                    $dataProviderClassName = $className;
                }

                $dataProviderClass  = new ReflectionClass($dataProviderClassName);
                $dataProviderMethod = $dataProviderClass->getMethod(
                    $dataProviderMethodName
                );

                if ($dataProviderMethod->isStatic()) {
                    $object = null;
                } else {
                    $object = $dataProviderClass->newInstance();
                }

                if ($dataProviderMethod->getNumberOfParameters() == 0) {
                    $data = $dataProviderMethod->invoke($object);
                } else {
                    $data = $dataProviderMethod->invoke($object, $methodName);
                }

                if ($data instanceof Iterator) {
                    $data = iterator_to_array($data);
                }

                if (is_array($data)) {
                    $result = array_merge($result, $data);
                }
            }

            return $result;
        }
    }

    /**
     * @param string $docComment full docComment string
     *
     * @return array when @testWith annotation is defined
     *               null  when @testWith annotation is omitted
     *
     * @throws PHPUnit_Framework_Exception when @testWith annotation is defined but cannot be parsed
     */
    public static function getDataFromTestWithAnnotation($docComment)
    {
        $docComment = self::cleanUpMultiLineAnnotation($docComment);

        if (preg_match(self::REGEX_TEST_WITH, $docComment, $matches, PREG_OFFSET_CAPTURE)) {
            $offset            = strlen($matches[0][0]) + $matches[0][1];
            $annotationContent = substr($docComment, $offset);
            $data              = [];

            foreach (explode("\n", $annotationContent) as $candidateRow) {
                $candidateRow = trim($candidateRow);

                if ($candidateRow[0] !== '[') {
                    break;
                }

                $dataSet = json_decode($candidateRow, true);

                if (json_last_error() != JSON_ERROR_NONE) {
                    throw new PHPUnit_Framework_Exception(
                        'The dataset for the @testWith annotation cannot be parsed: ' . json_last_error_msg()
                    );
                }

                $data[] = $dataSet;
            }

            if (!$data) {
                throw new PHPUnit_Framework_Exception('The dataset for the @testWith annotation cannot be parsed.');
            }

            return $data;
        }
    }

    private static function cleanUpMultiLineAnnotation($docComment)
    {
        //removing initial '   * ' for docComment
        $docComment = str_replace("\r\n", "\n", $docComment);
        $docComment = preg_replace('/' . '\n' . '\s*' . '\*' . '\s?' . '/', "\n", $docComment);
        $docComment = substr($docComment, 0, -1);
        $docComment = rtrim($docComment, "\n");

        return $docComment;
    }

    /**
     * @param string $className
     * @param string $methodName
     *
     * @return array
     *
     * @throws ReflectionException
     */
    public static function parseTestMethodAnnotations($className, $methodName = '')
    {
        if (!isset(self::$annotationCache[$className])) {
            $class                             = new ReflectionClass($className);
            self::$annotationCache[$className] = self::parseAnnotations($class->getDocComment());
        }

        if (!empty($methodName) && !isset(self::$annotationCache[$className . '::' . $methodName])) {
            try {
                $method      = new ReflectionMethod($className, $methodName);
                $annotations = self::parseAnnotations($method->getDocComment());
            } catch (ReflectionException $e) {
                $annotations = [];
            }
            self::$annotationCache[$className . '::' . $methodName] = $annotations;
        }

        return [
          'class'  => self::$annotationCache[$className],
          'method' => !empty($methodName) ? self::$annotationCache[$className . '::' . $methodName] : []
        ];
    }

    /**
     * @param string $className
     * @param string $methodName
     *
     * @return array
     */
    public static function getInlineAnnotations($className, $methodName)
    {
        $method      = new ReflectionMethod($className, $methodName);
        $code        = file($method->getFileName());
        $lineNumber  = $method->getStartLine();
        $startLine   = $method->getStartLine() - 1;
        $endLine     = $method->getEndLine() - 1;
        $methodLines = array_slice($code, $startLine, $endLine - $startLine + 1);
        $annotations = [];

        foreach ($methodLines as $line) {
            if (preg_match('#/\*\*?\s*@(?P<name>[A-Za-z_-]+)(?:[ \t]+(?P<value>.*?))?[ \t]*\r?\*/$#m', $line, $matches)) {
                $annotations[strtolower($matches['name'])] = [
                    'line'  => $lineNumber,
                    'value' => $matches['value']
                ];
            }

            $lineNumber++;
        }

        return $annotations;
    }

    /**
     * @param string $docblock
     *
     * @return array
     */
    private static function parseAnnotations($docblock)
    {
        $annotations = [];
        // Strip away the docblock header and footer to ease parsing of one line annotations
        $docblock = substr($docblock, 3, -2);

        if (preg_match_all('/@(?P<name>[A-Za-z_-]+)(?:[ \t]+(?P<value>.*?))?[ \t]*\r?$/m', $docblock, $matches)) {
            $numMatches = count($matches[0]);

            for ($i = 0; $i < $numMatches; ++$i) {
                $annotations[$matches['name'][$i]][] = (string) $matches['value'][$i];
            }
        }

        return $annotations;
    }

    /**
     * Returns the backup settings for a test.
     *
     * @param string $className
     * @param string $methodName
     *
     * @return array
     */
    public static function getBackupSettings($className, $methodName)
    {
        return [
          'backupGlobals' => self::getBooleanAnnotationSetting(
              $className,
              $methodName,
              'backupGlobals'
          ),
          'backupStaticAttributes' => self::getBooleanAnnotationSetting(
              $className,
              $methodName,
              'backupStaticAttributes'
          )
        ];
    }

    /**
     * Returns the dependencies for a test class or method.
     *
     * @param string $className
     * @param string $methodName
     *
     * @return array
     */
    public static function getDependencies($className, $methodName)
    {
        $annotations = self::parseTestMethodAnnotations(
            $className,
            $methodName
        );

        $dependencies = [];

        if (isset($annotations['class']['depends'])) {
            $dependencies = $annotations['class']['depends'];
        }

        if (isset($annotations['method']['depends'])) {
            $dependencies = array_merge(
                $dependencies,
                $annotations['method']['depends']
            );
        }

        return array_unique($dependencies);
    }

    /**
     * Returns the error handler settings for a test.
     *
     * @param string $className
     * @param string $methodName
     *
     * @return bool
     */
    public static function getErrorHandlerSettings($className, $methodName)
    {
        return self::getBooleanAnnotationSetting(
            $className,
            $methodName,
            'errorHandler'
        );
    }

    /**
     * Returns the groups for a test class or method.
     *
     * @param string $className
     * @param string $methodName
     *
     * @return array
     */
    public static function getGroups($className, $methodName = '')
    {
        $annotations = self::parseTestMethodAnnotations(
            $className,
            $methodName
        );

        $groups = [];

        if (isset($annotations['method']['author'])) {
            $groups = $annotations['method']['author'];
        } elseif (isset($annotations['class']['author'])) {
            $groups = $annotations['class']['author'];
        }

        if (isset($annotations['class']['group'])) {
            $groups = array_merge($groups, $annotations['class']['group']);
        }

        if (isset($annotations['method']['group'])) {
            $groups = array_merge($groups, $annotations['method']['group']);
        }

        if (isset($annotations['class']['ticket'])) {
            $groups = array_merge($groups, $annotations['class']['ticket']);
        }

        if (isset($annotations['method']['ticket'])) {
            $groups = array_merge($groups, $annotations['method']['ticket']);
        }

        foreach (['method', 'class'] as $element) {
            foreach (['small', 'medium', 'large'] as $size) {
                if (isset($annotations[$element][$size])) {
                    $groups[] = $size;
                    break 2;
                }
            }
        }

        return array_unique($groups);
    }

    /**
     * Returns the size of the test.
     *
     * @param string $className
     * @param string $methodName
     *
     * @return int
     */
    public static function getSize($className, $methodName)
    {
        $groups = array_flip(self::getGroups($className, $methodName));
        $size   = self::UNKNOWN;
        $class  = new ReflectionClass($className);

        if (isset($groups['large']) ||
            (class_exists('PHPUnit_Extensions_Database_TestCase', false) &&
             $class->isSubclassOf('PHPUnit_Extensions_Database_TestCase'))) {
            $size = self::LARGE;
        } elseif (isset($groups['medium'])) {
            $size = self::MEDIUM;
        } elseif (isset($groups['small'])) {
            $size = self::SMALL;
        }

        return $size;
    }

    /**
     * Returns the tickets for a test class or method.
     *
     * @param string $className
     * @param string $methodName
     *
     * @return array
     */
    public static function getTickets($className, $methodName)
    {
        $annotations = self::parseTestMethodAnnotations(
            $className,
            $methodName
        );

        $tickets = [];

        if (isset($annotations['class']['ticket'])) {
            $tickets = $annotations['class']['ticket'];
        }

        if (isset($annotations['method']['ticket'])) {
            $tickets = array_merge($tickets, $annotations['method']['ticket']);
        }

        return array_unique($tickets);
    }

    /**
     * Returns the process isolation settings for a test.
     *
     * @param string $className
     * @param string $methodName
     *
     * @return bool
     */
    public static function getProcessIsolationSettings($className, $methodName)
    {
        $annotations = self::parseTestMethodAnnotations(
            $className,
            $methodName
        );

        if (isset($annotations['class']['runTestsInSeparateProcesses']) ||
            isset($annotations['method']['runInSeparateProcess'])) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Returns the preserve global state settings for a test.
     *
     * @param string $className
     * @param string $methodName
     *
     * @return bool
     */
    public static function getPreserveGlobalStateSettings($className, $methodName)
    {
        return self::getBooleanAnnotationSetting(
            $className,
            $methodName,
            'preserveGlobalState'
        );
    }

    /**
     * @param string $className
     *
     * @return array
     */
    public static function getHookMethods($className)
    {
        if (!class_exists($className, false)) {
            return self::emptyHookMethodsArray();
        }

        if (!isset(self::$hookMethods[$className])) {
            self::$hookMethods[$className] = self::emptyHookMethodsArray();

            try {
                $class = new ReflectionClass($className);

                foreach ($class->getMethods() as $method) {
                    if (self::isBeforeClassMethod($method)) {
                        self::$hookMethods[$className]['beforeClass'][] = $method->getName();
                    }

                    if (self::isBeforeMethod($method)) {
                        self::$hookMethods[$className]['before'][] = $method->getName();
                    }

                    if (self::isAfterMethod($method)) {
                        self::$hookMethods[$className]['after'][] = $method->getName();
                    }

                    if (self::isAfterClassMethod($method)) {
                        self::$hookMethods[$className]['afterClass'][] = $method->getName();
                    }
                }
            } catch (ReflectionException $e) {
            }
        }

        return self::$hookMethods[$className];
    }

    /**
     * @return array
     */
    private static function emptyHookMethodsArray()
    {
        return [
            'beforeClass' => ['setUpBeforeClass'],
            'before'      => ['setUp'],
            'after'       => ['tearDown'],
            'afterClass'  => ['tearDownAfterClass']
        ];
    }

    /**
     * @param string $className
     * @param string $methodName
     * @param string $settingName
     *
     * @return bool
     */
    private static function getBooleanAnnotationSetting($className, $methodName, $settingName)
    {
        $annotations = self::parseTestMethodAnnotations(
            $className,
            $methodName
        );

        $result = null;

        if (isset($annotations['class'][$settingName])) {
            if ($annotations['class'][$settingName][0] == 'enabled') {
                $result = true;
            } elseif ($annotations['class'][$settingName][0] == 'disabled') {
                $result = false;
            }
        }

        if (isset($annotations['method'][$settingName])) {
            if ($annotations['method'][$settingName][0] == 'enabled') {
                $result = true;
            } elseif ($annotations['method'][$settingName][0] == 'disabled') {
                $result = false;
            }
        }

        return $result;
    }

    /**
     * @param string $element
     *
     * @return array
     *
     * @throws PHPUnit_Framework_InvalidCoversTargetException
     */
    private static function resolveElementToReflectionObjects($element)
    {
        $codeToCoverList = [];

        if (strpos($element, '\\') !== false && function_exists($element)) {
            $codeToCoverList[] = new ReflectionFunction($element);
        } elseif (strpos($element, '::') !== false) {
            list($className, $methodName) = explode('::', $element);

            if (isset($methodName[0]) && $methodName[0] == '<') {
                $classes = [$className];

                foreach ($classes as $className) {
                    if (!class_exists($className) &&
                        !interface_exists($className) &&
                        !trait_exists($className)) {
                        throw new PHPUnit_Framework_InvalidCoversTargetException(
                            sprintf(
                                'Trying to @cover or @use not existing class or ' .
                                'interface "%s".',
                                $className
                            )
                        );
                    }

                    $class   = new ReflectionClass($className);
                    $methods = $class->getMethods();
                    $inverse = isset($methodName[1]) && $methodName[1] == '!';

                    if (strpos($methodName, 'protected')) {
                        $visibility = 'isProtected';
                    } elseif (strpos($methodName, 'private')) {
                        $visibility = 'isPrivate';
                    } elseif (strpos($methodName, 'public')) {
                        $visibility = 'isPublic';
                    }

                    foreach ($methods as $method) {
                        if ($inverse && !$method->$visibility()) {
                            $codeToCoverList[] = $method;
                        } elseif (!$inverse && $method->$visibility()) {
                            $codeToCoverList[] = $method;
                        }
                    }
                }
            } else {
                $classes = [$className];

                foreach ($classes as $className) {
                    if ($className == '' && function_exists($methodName)) {
                        $codeToCoverList[] = new ReflectionFunction(
                            $methodName
                        );
                    } else {
                        if (!((class_exists($className) ||
                               interface_exists($className) ||
                               trait_exists($className)) &&
                              method_exists($className, $methodName))) {
                            throw new PHPUnit_Framework_InvalidCoversTargetException(
                                sprintf(
                                    'Trying to @cover or @use not existing method "%s::%s".',
                                    $className,
                                    $methodName
                                )
                            );
                        }

                        $codeToCoverList[] = new ReflectionMethod(
                            $className,
                            $methodName
                        );
                    }
                }
            }
        } else {
            $extended = false;

            if (strpos($element, '<extended>') !== false) {
                $element  = str_replace('<extended>', '', $element);
                $extended = true;
            }

            $classes = [$element];

            if ($extended) {
                $classes = array_merge(
                    $classes,
                    class_implements($element),
                    class_parents($element)
                );
            }

            foreach ($classes as $className) {
                if (!class_exists($className) &&
                    !interface_exists($className) &&
                    !trait_exists($className)) {
                    throw new PHPUnit_Framework_InvalidCoversTargetException(
                        sprintf(
                            'Trying to @cover or @use not existing class or ' .
                            'interface "%s".',
                            $className
                        )
                    );
                }

                $codeToCoverList[] = new ReflectionClass($className);
            }
        }

        return $codeToCoverList;
    }

    /**
     * @param array $reflectors
     *
     * @return array
     */
    private static function resolveReflectionObjectsToLines(array $reflectors)
    {
        $result = [];

        foreach ($reflectors as $reflector) {
            $filename = $reflector->getFileName();

            if (!isset($result[$filename])) {
                $result[$filename] = [];
            }

            $result[$filename] = array_merge(
                $result[$filename],
                range($reflector->getStartLine(), $reflector->getEndLine())
            );
        }

        foreach ($result as $filename => $lineNumbers) {
            $result[$filename] = array_keys(array_flip($lineNumbers));
        }

        return $result;
    }

    /**
     * @param ReflectionMethod $method
     *
     * @return bool
     */
    private static function isBeforeClassMethod(ReflectionMethod $method)
    {
        return $method->isStatic() && strpos($method->getDocComment(), '@beforeClass') !== false;
    }

    /**
     * @param ReflectionMethod $method
     *
     * @return bool
     */
    private static function isBeforeMethod(ReflectionMethod $method)
    {
        return preg_match('/@before\b/', $method->getDocComment());
    }

    /**
     * @param ReflectionMethod $method
     *
     * @return bool
     */
    private static function isAfterClassMethod(ReflectionMethod $method)
    {
        return $method->isStatic() && strpos($method->getDocComment(), '@afterClass') !== false;
    }

    /**
     * @param ReflectionMethod $method
     *
     * @return bool
     */
    private static function isAfterMethod(ReflectionMethod $method)
    {
        return preg_match('/@after\b/', $method->getDocComment());
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Prettifies class and method names for use in TestDox documentation.
 */
class PHPUnit_Util_TestDox_NamePrettifier
{
    /**
     * @var string
     */
    protected $prefix = 'Test';

    /**
     * @var string
     */
    protected $suffix = 'Test';

    /**
     * @var array
     */
    protected $strings = [];

    /**
     * Prettifies the name of a test class.
     *
     * @param string $name
     *
     * @return string
     */
    public function prettifyTestClass($name)
    {
        $title = $name;

        if ($this->suffix !== null &&
            $this->suffix == substr($name, -1 * strlen($this->suffix))) {
            $title = substr($title, 0, strripos($title, $this->suffix));
        }

        if ($this->prefix !== null &&
            $this->prefix == substr($name, 0, strlen($this->prefix))) {
            $title = substr($title, strlen($this->prefix));
        }

        if (substr($title, 0, 1) == '\\') {
            $title = substr($title, 1);
        }

        return $title;
    }

    /**
     * Prettifies the name of a test method.
     *
     * @param string $name
     *
     * @return string
     */
    public function prettifyTestMethod($name)
    {
        $buffer = '';

        if (!is_string($name) || strlen($name) == 0) {
            return $buffer;
        }

        $string = preg_replace('#\d+$#', '', $name, -1, $count);

        if (in_array($string, $this->strings)) {
            $name = $string;
        } elseif ($count == 0) {
            $this->strings[] = $string;
        }

        if (substr($name, 0, 4) == 'test') {
            $name = substr($name, 4);
        }

        if (strlen($name) == 0) {
            return $buffer;
        }

        $name[0] = strtoupper($name[0]);

        if (strpos($name, '_') !== false) {
            return trim(str_replace('_', ' ', $name));
        }

        $max        = strlen($name);
        $wasNumeric = false;

        for ($i = 0; $i < $max; $i++) {
            if ($i > 0 &&
                ord($name[$i]) >= 65 &&
                ord($name[$i]) <= 90) {
                $buffer .= ' ' . strtolower($name[$i]);
            } else {
                $isNumeric = is_numeric($name[$i]);

                if (!$wasNumeric && $isNumeric) {
                    $buffer    .= ' ';
                    $wasNumeric = true;
                }

                if ($wasNumeric && !$isNumeric) {
                    $wasNumeric = false;
                }

                $buffer .= $name[$i];
            }
        }

        return $buffer;
    }

    /**
     * Sets the prefix of test names.
     *
     * @param string $prefix
     */
    public function setPrefix($prefix)
    {
        $this->prefix = $prefix;
    }

    /**
     * Sets the suffix of test names.
     *
     * @param string $suffix
     */
    public function setSuffix($suffix)
    {
        $this->suffix = $suffix;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Prints TestDox documentation in HTML format.
 */
class PHPUnit_Util_TestDox_ResultPrinter_HTML extends PHPUnit_Util_TestDox_ResultPrinter
{
    /**
     * @var string
     */
    private $pageHeader = <<<EOT
<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8"/>
        <title>Test Documentation</title>
        <style>
            body {
                text-rendering: optimizeLegibility;
                font-variant-ligatures: common-ligatures;
                font-kerning: normal;
                margin-left: 2em;
            }

            body > ul > li {
                font-family: Source Serif Pro, PT Sans, Trebuchet MS, Helvetica, Arial;
                font-size: 2em;
            }

            h2 {
                font-family: Tahoma, Helvetica, Arial;
                font-size: 3em;
            }

            ul {
                list-style: none;
                margin-bottom: 1em;
            }
        </style>
    </head>
    <body>
EOT;

    /**
     * @var string
     */
    private $classHeader = <<<EOT

        <h2 id="%s">%s</h2>
        <ul>

EOT;

    /**
     * @var string
     */
    private $classFooter = <<<EOT
        </ul>
EOT;

    /**
     * @var string
     */
    private $pageFooter = <<<EOT

    </body>
</html>
EOT;

    /**
     * Handler for 'start run' event.
     */
    protected function startRun()
    {
        $this->write($this->pageHeader);
    }

    /**
     * Handler for 'start class' event.
     *
     * @param string $name
     */
    protected function startClass($name)
    {
        $this->write(
            sprintf(
                $this->classHeader,
                $name,
                $this->currentTestClassPrettified
            )
        );
    }

    /**
     * Handler for 'on test' event.
     *
     * @param string $name
     * @param bool   $success
     */
    protected function onTest($name, $success = true)
    {
        $this->write(
            sprintf(
                "            <li style=\"color: %s;\">%s %s</li>\n",
                $success ? '#555753' : '#ef2929',
                $success ? '✓' : '❌',
                $name
            )
        );
    }

    /**
     * Handler for 'end class' event.
     *
     * @param string $name
     */
    protected function endClass($name)
    {
        $this->write($this->classFooter);
    }

    /**
     * Handler for 'end run' event.
     */
    protected function endRun()
    {
        $this->write($this->pageFooter);
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Prints TestDox documentation in text format.
 */
class PHPUnit_Util_TestDox_ResultPrinter_Text extends PHPUnit_Util_TestDox_ResultPrinter
{
    /**
     * Handler for 'start class' event.
     *
     * @param string $name
     */
    protected function startClass($name)
    {
        $this->write($this->currentTestClassPrettified . "\n");
    }

    /**
     * Handler for 'on test' event.
     *
     * @param string $name
     * @param bool   $success
     */
    protected function onTest($name, $success = true)
    {
        if ($success) {
            $this->write(' [x] ');
        } else {
            $this->write(' [ ] ');
        }

        $this->write($name . "\n");
    }

    /**
     * Handler for 'end class' event.
     *
     * @param string $name
     */
    protected function endClass($name)
    {
        $this->write("\n");
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

class PHPUnit_Util_TestDox_ResultPrinter_XML extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
{
    /**
     * @var DOMDocument
     */
    private $document;

    /**
     * @var DOMElement
     */
    private $root;

    /**
     * @var PHPUnit_Util_TestDox_NamePrettifier
     */
    private $prettifier;

    /**
     * @var Exception
     */
    private $exception;

    /**
     * @param string|resource $out
     */
    public function __construct($out = null)
    {
        $this->document               = new DOMDocument('1.0', 'UTF-8');
        $this->document->formatOutput = true;

        $this->root = $this->document->createElement('tests');
        $this->document->appendChild($this->root);

        $this->prettifier = new PHPUnit_Util_TestDox_NamePrettifier;

        parent::__construct($out);
    }

    /**
     * Flush buffer and close output.
     */
    public function flush()
    {
        $this->write($this->document->saveXML());

        parent::flush();
    }

    /**
     * An error occurred.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->exception = $e;
    }

    /**
     * A warning occurred.
     *
     * @param PHPUnit_Framework_Test    $test
     * @param PHPUnit_Framework_Warning $e
     * @param float                     $time
     */
    public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
    {
    }

    /**
     * A failure occurred.
     *
     * @param PHPUnit_Framework_Test                 $test
     * @param PHPUnit_Framework_AssertionFailedError $e
     * @param float                                  $time
     */
    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
    {
        $this->exception = $e;
    }

    /**
     * Incomplete test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
    }

    /**
     * Risky test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
    }

    /**
     * Skipped test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
    }

    /**
     * A test suite started.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
    }

    /**
     * A test suite ended.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
    }

    /**
     * A test started.
     *
     * @param PHPUnit_Framework_Test $test
     */
    public function startTest(PHPUnit_Framework_Test $test)
    {
        $this->exception = null;
    }

    /**
     * A test ended.
     *
     * @param PHPUnit_Framework_Test $test
     * @param float                  $time
     */
    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
        if (!$test instanceof PHPUnit_Framework_TestCase) {
            return;
        }

        /* @var PHPUnit_Framework_TestCase $test */

        $groups = array_filter(
            $test->getGroups(),
            function ($group) {
                if ($group == 'small' || $group == 'medium' || $group == 'large') {
                    return false;
                }

                return true;
            }
        );

        $node = $this->document->createElement('test');

        $node->setAttribute('className', get_class($test));
        $node->setAttribute('methodName', $test->getName());
        $node->setAttribute('prettifiedClassName', $this->prettifier->prettifyTestClass(get_class($test)));
        $node->setAttribute('prettifiedMethodName', $this->prettifier->prettifyTestMethod($test->getName()));
        $node->setAttribute('status', $test->getStatus());
        $node->setAttribute('time', $time);
        $node->setAttribute('size', $test->getSize());
        $node->setAttribute('groups', implode(',', $groups));

        $inlineAnnotations = PHPUnit_Util_Test::getInlineAnnotations(get_class($test), $test->getName());

        if (isset($inlineAnnotations['given']) && isset($inlineAnnotations['when']) && isset($inlineAnnotations['then'])) {
            $node->setAttribute('given', $inlineAnnotations['given']['value']);
            $node->setAttribute('givenStartLine', $inlineAnnotations['given']['line']);
            $node->setAttribute('when', $inlineAnnotations['when']['value']);
            $node->setAttribute('whenStartLine', $inlineAnnotations['when']['line']);
            $node->setAttribute('then', $inlineAnnotations['then']['value']);
            $node->setAttribute('thenStartLine', $inlineAnnotations['then']['line']);
        }

        if ($this->exception !== null) {
            if ($this->exception instanceof PHPUnit_Framework_Exception) {
                $steps = $this->exception->getSerializableTrace();
            } else {
                $steps = $this->exception->getTrace();
            }

            $class = new ReflectionClass($test);
            $file  = $class->getFileName();

            foreach ($steps as $step) {
                if (isset($step['file']) && $step['file'] == $file) {
                    $node->setAttribute('exceptionLine', $step['line']);

                    break;
                }
            }

            $node->setAttribute('exceptionMessage', $this->exception->getMessage());
        }

        $this->root->appendChild($node);
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Base class for printers of TestDox documentation.
 */
abstract class PHPUnit_Util_TestDox_ResultPrinter extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
{
    /**
     * @var PHPUnit_Util_TestDox_NamePrettifier
     */
    protected $prettifier;

    /**
     * @var string
     */
    protected $testClass = '';

    /**
     * @var int
     */
    protected $testStatus = false;

    /**
     * @var array
     */
    protected $tests = [];

    /**
     * @var int
     */
    protected $successful = 0;

    /**
     * @var int
     */
    protected $warned = 0;

    /**
     * @var int
     */
    protected $failed = 0;

    /**
     * @var int
     */
    protected $risky = 0;

    /**
     * @var int
     */
    protected $skipped = 0;

    /**
     * @var int
     */
    protected $incomplete = 0;

    /**
     * @var string
     */
    protected $currentTestClassPrettified;

    /**
     * @var string
     */
    protected $currentTestMethodPrettified;

    /**
     * @var array
     */
    private $groups;

    /**
     * @var array
     */
    private $excludeGroups;

    /**
     * @param resource $out
     * @param array    $groups
     * @param array    $excludeGroups
     */
    public function __construct($out = null, array $groups = [], array $excludeGroups = [])
    {
        parent::__construct($out);

        $this->groups        = $groups;
        $this->excludeGroups = $excludeGroups;

        $this->prettifier = new PHPUnit_Util_TestDox_NamePrettifier;
        $this->startRun();
    }

    /**
     * Flush buffer and close output.
     */
    public function flush()
    {
        $this->doEndClass();
        $this->endRun();

        parent::flush();
    }

    /**
     * An error occurred.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        if (!$this->isOfInterest($test)) {
            return;
        }

        $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_ERROR;
        $this->failed++;
    }

    /**
     * A warning occurred.
     *
     * @param PHPUnit_Framework_Test    $test
     * @param PHPUnit_Framework_Warning $e
     * @param float                     $time
     */
    public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
    {
        if (!$this->isOfInterest($test)) {
            return;
        }

        $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_WARNING;
        $this->warned++;
    }

    /**
     * A failure occurred.
     *
     * @param PHPUnit_Framework_Test                 $test
     * @param PHPUnit_Framework_AssertionFailedError $e
     * @param float                                  $time
     */
    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
    {
        if (!$this->isOfInterest($test)) {
            return;
        }

        $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE;
        $this->failed++;
    }

    /**
     * Incomplete test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        if (!$this->isOfInterest($test)) {
            return;
        }

        $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE;
        $this->incomplete++;
    }

    /**
     * Risky test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        if (!$this->isOfInterest($test)) {
            return;
        }

        $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_RISKY;
        $this->risky++;
    }

    /**
     * Skipped test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     */
    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        if (!$this->isOfInterest($test)) {
            return;
        }

        $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED;
        $this->skipped++;
    }

    /**
     * A testsuite started.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
    }

    /**
     * A testsuite ended.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
    }

    /**
     * A test started.
     *
     * @param PHPUnit_Framework_Test $test
     */
    public function startTest(PHPUnit_Framework_Test $test)
    {
        if (!$this->isOfInterest($test)) {
            return;
        }

        $class = get_class($test);

        if ($this->testClass != $class) {
            if ($this->testClass != '') {
                $this->doEndClass();
            }

            $classAnnotations = PHPUnit_Util_Test::parseTestMethodAnnotations($class);
            if (isset($classAnnotations['class']['testdox'][0])) {
                $this->currentTestClassPrettified = $classAnnotations['class']['testdox'][0];
            } else {
                $this->currentTestClassPrettified = $this->prettifier->prettifyTestClass($class);
            }

            $this->startClass($class);

            $this->testClass = $class;
            $this->tests     = [];
        }

        $annotations = $test->getAnnotations();

        if (isset($annotations['method']['testdox'][0])) {
            $this->currentTestMethodPrettified = $annotations['method']['testdox'][0];
        } else {
            $this->currentTestMethodPrettified = $this->prettifier->prettifyTestMethod($test->getName(false));
        }

        if ($test instanceof PHPUnit_Framework_TestCase && $test->usesDataProvider()) {
            $this->currentTestMethodPrettified .= ' ' . $test->dataDescription();
        }

        $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_PASSED;
    }

    /**
     * A test ended.
     *
     * @param PHPUnit_Framework_Test $test
     * @param float                  $time
     */
    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
        if (!$this->isOfInterest($test)) {
            return;
        }

        if (!isset($this->tests[$this->currentTestMethodPrettified])) {
            if ($this->testStatus == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) {
                $this->tests[$this->currentTestMethodPrettified]['success'] = 1;
                $this->tests[$this->currentTestMethodPrettified]['failure'] = 0;
            } else {
                $this->tests[$this->currentTestMethodPrettified]['success'] = 0;
                $this->tests[$this->currentTestMethodPrettified]['failure'] = 1;
            }
        } else {
            if ($this->testStatus == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) {
                $this->tests[$this->currentTestMethodPrettified]['success']++;
            } else {
                $this->tests[$this->currentTestMethodPrettified]['failure']++;
            }
        }

        $this->currentTestClassPrettified  = null;
        $this->currentTestMethodPrettified = null;
    }

    protected function doEndClass()
    {
        foreach ($this->tests as $name => $data) {
            $this->onTest($name, $data['failure'] == 0);
        }

        $this->endClass($this->testClass);
    }

    /**
     * Handler for 'start run' event.
     */
    protected function startRun()
    {
    }

    /**
     * Handler for 'start class' event.
     *
     * @param string $name
     */
    protected function startClass($name)
    {
    }

    /**
     * Handler for 'on test' event.
     *
     * @param string $name
     * @param bool   $success
     */
    protected function onTest($name, $success = true)
    {
    }

    /**
     * Handler for 'end class' event.
     *
     * @param string $name
     */
    protected function endClass($name)
    {
    }

    /**
     * Handler for 'end run' event.
     */
    protected function endRun()
    {
    }

    /**
     * @param PHPUnit_Framework_Test $test
     *
     * @return bool
     */
    private function isOfInterest(PHPUnit_Framework_Test $test)
    {
        if (!$test instanceof PHPUnit_Framework_TestCase) {
            return false;
        }

        if ($test instanceof PHPUnit_Framework_WarningTestCase) {
            return false;
        }

        if (!empty($this->groups)) {
            foreach ($test->getGroups() as $group) {
                if (in_array($group, $this->groups)) {
                    return true;
                }
            }

            return false;
        }

        if (!empty($this->excludeGroups)) {
            foreach ($test->getGroups() as $group) {
                if (in_array($group, $this->excludeGroups)) {
                    return false;
                }
            }

            return true;
        }

        return true;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Iterator for test suites.
 */
class PHPUnit_Util_TestSuiteIterator implements RecursiveIterator
{
    /**
     * @var int
     */
    protected $position;

    /**
     * @var PHPUnit_Framework_Test[]
     */
    protected $tests;

    /**
     * @param PHPUnit_Framework_TestSuite $testSuite
     */
    public function __construct(PHPUnit_Framework_TestSuite $testSuite)
    {
        $this->tests = $testSuite->tests();
    }

    /**
     * Rewinds the Iterator to the first element.
     */
    public function rewind()
    {
        $this->position = 0;
    }

    /**
     * Checks if there is a current element after calls to rewind() or next().
     *
     * @return bool
     */
    public function valid()
    {
        return $this->position < count($this->tests);
    }

    /**
     * Returns the key of the current element.
     *
     * @return int
     */
    public function key()
    {
        return $this->position;
    }

    /**
     * Returns the current element.
     *
     * @return PHPUnit_Framework_Test
     */
    public function current()
    {
        return $this->valid() ? $this->tests[$this->position] : null;
    }

    /**
     * Moves forward to next element.
     */
    public function next()
    {
        $this->position++;
    }

    /**
     * Returns the sub iterator for the current element.
     *
     * @return PHPUnit_Util_TestSuiteIterator
     */
    public function getChildren()
    {
        return new self(
            $this->tests[$this->position]
        );
    }

    /**
     * Checks whether the current element has children.
     *
     * @return bool
     */
    public function hasChildren()
    {
        return $this->tests[$this->position] instanceof PHPUnit_Framework_TestSuite;
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Utility class for textual type (and value) representation.
 */
class PHPUnit_Util_Type
{
    /**
     * @param string $type
     *
     * @return bool
     */
    public static function isType($type)
    {
        return in_array(
            $type,
            [
                'numeric',
                'integer',
                'int',
                'float',
                'string',
                'boolean',
                'bool',
                'null',
                'array',
                'object',
                'resource',
                'scalar'
            ]
        );
    }
}
<?php
/*
 * This file is part of PHPUnit.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * XML helpers.
 */
class PHPUnit_Util_XML
{
    /**
     * Load an $actual document into a DOMDocument.  This is called
     * from the selector assertions.
     *
     * If $actual is already a DOMDocument, it is returned with
     * no changes.  Otherwise, $actual is loaded into a new DOMDocument
     * as either HTML or XML, depending on the value of $isHtml. If $isHtml is
     * false and $xinclude is true, xinclude is performed on the loaded
     * DOMDocument.
     *
     * Note: prior to PHPUnit 3.3.0, this method loaded a file and
     * not a string as it currently does.  To load a file into a
     * DOMDocument, use loadFile() instead.
     *
     * @param string|DOMDocument $actual
     * @param bool               $isHtml
     * @param string             $filename
     * @param bool               $xinclude
     * @param bool               $strict
     *
     * @return DOMDocument
     */
    public static function load($actual, $isHtml = false, $filename = '', $xinclude = false, $strict = false)
    {
        if ($actual instanceof DOMDocument) {
            return $actual;
        }

        if (!is_string($actual)) {
            throw new PHPUnit_Framework_Exception('Could not load XML from ' . gettype($actual));
        }

        if ($actual === '') {
            throw new PHPUnit_Framework_Exception('Could not load XML from empty string');
        }

        // Required for XInclude on Windows.
        if ($xinclude) {
            $cwd = getcwd();
            @chdir(dirname($filename));
        }

        $document                     = new DOMDocument;
        $document->preserveWhiteSpace = false;

        $internal  = libxml_use_internal_errors(true);
        $message   = '';
        $reporting = error_reporting(0);

        if ('' !== $filename) {
            // Necessary for xinclude
            $document->documentURI = $filename;
        }

        if ($isHtml) {
            $loaded = $document->loadHTML($actual);
        } else {
            $loaded = $document->loadXML($actual);
        }

        if (!$isHtml && $xinclude) {
            $document->xinclude();
        }

        foreach (libxml_get_errors() as $error) {
            $message .= "\n" . $error->message;
        }

        libxml_use_internal_errors($internal);
        error_reporting($reporting);

        if ($xinclude) {
            @chdir($cwd);
        }

        if ($loaded === false || ($strict && $message !== '')) {
            if ($filename !== '') {
                throw new PHPUnit_Framework_Exception(
                    sprintf(
                        'Could not load "%s".%s',
                        $filename,
                        $message != '' ? "\n" . $message : ''
                    )
                );
            } else {
                if ($message === '') {
                    $message = 'Could not load XML for unknown reason';
                }
                throw new PHPUnit_Framework_Exception($message);
            }
        }

        return $document;
    }

    /**
     * Loads an XML (or HTML) file into a DOMDocument object.
     *
     * @param string $filename
     * @param bool   $isHtml
     * @param bool   $xinclude
     * @param bool   $strict
     *
     * @return DOMDocument
     */
    public static function loadFile($filename, $isHtml = false, $xinclude = false, $strict = false)
    {
        $reporting = error_reporting(0);
        $contents  = file_get_contents($filename);
        error_reporting($reporting);

        if ($contents === false) {
            throw new PHPUnit_Framework_Exception(
                sprintf(
                    'Could not read "%s".',
                    $filename
                )
            );
        }

        return self::load($contents, $isHtml, $filename, $xinclude, $strict);
    }

    /**
     * @param DOMNode $node
     */
    public static function removeCharacterDataNodes(DOMNode $node)
    {
        if ($node->hasChildNodes()) {
            for ($i = $node->childNodes->length - 1; $i >= 0; $i--) {
                if (($child = $node->childNodes->item($i)) instanceof DOMCharacterData) {
                    $node->removeChild($child);
                }
            }
        }
    }

    /**
     * Escapes a string for the use in XML documents
     * Any Unicode character is allowed, excluding the surrogate blocks, FFFE,
     * and FFFF (not even as character reference).
     * See http://www.w3.org/TR/xml/#charsets
     *
     * @param string $string
     *
     * @return string
     */
    public static function prepareString($string)
    {
        return preg_replace(
            '/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/',
            '',
            htmlspecialchars(
                PHPUnit_Util_String::convertToUtf8($string),
                ENT_QUOTES,
                'UTF-8'
            )
        );
    }

    /**
     * "Convert" a DOMElement object into a PHP variable.
     *
     * @param DOMElement $element
     *
     * @return mixed
     */
    public static function xmlToVariable(DOMElement $element)
    {
        $variable = null;

        switch ($element->tagName) {
            case 'array':
                $variable = [];

                foreach ($element->childNodes as $entry) {
                    if (!$entry instanceof DOMElement || $entry->tagName !== 'element') {
                        continue;
                    }
                    $item = $entry->childNodes->item(0);

                    if ($item instanceof DOMText) {
                        $item = $entry->childNodes->item(1);
                    }

                    $value = self::xmlToVariable($item);

                    if ($entry->hasAttribute('key')) {
                        $variable[(string) $entry->getAttribute('key')] = $value;
                    } else {
                        $variable[] = $value;
                    }
                }
                break;

            case 'object':
                $className = $element->getAttribute('class');

                if ($element->hasChildNodes()) {
                    $arguments       = $element->childNodes->item(1)->childNodes;
                    $constructorArgs = [];

                    foreach ($arguments as $argument) {
                        if ($argument instanceof DOMElement) {
                            $constructorArgs[] = self::xmlToVariable($argument);
                        }
                    }

                    $class    = new ReflectionClass($className);
                    $variable = $class->newInstanceArgs($constructorArgs);
                } else {
                    $variable = new $className;
                }
                break;

            case 'boolean':
                $variable = $element->textContent == 'true' ? true : false;
                break;

            case 'integer':
            case 'double':
            case 'string':
                $variable = $element->textContent;

                settype($variable, $element->tagName);
                break;
        }

        return $variable;
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Builder interface for unique identifiers.
 *
 * Defines the interface for recording unique identifiers. The identifiers
 * can be used to define the invocation order of expectations. The expectation
 * is recorded using id() and then defined in order using
 * PHPUnit_Framework_MockObject_Builder_Match::after().
 *
 * @since Interface available since Release 1.0.0
 */
interface PHPUnit_Framework_MockObject_Builder_Identity
{
    /**
     * Sets the identification of the expectation to $id.
     *
     * @note The identifier is unique per mock object.
     *
     * @param string $id Unique identification of expectation.
     */
    public function id($id);
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Builder for mocked or stubbed invocations.
 *
 * Provides methods for building expectations without having to resort to
 * instantiating the various matchers manually. These methods also form a
 * more natural way of reading the expectation. This class should be together
 * with the test case PHPUnit_Framework_MockObject_TestCase.
 *
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_Builder_InvocationMocker implements PHPUnit_Framework_MockObject_Builder_MethodNameMatch
{
    /**
     * @var PHPUnit_Framework_MockObject_Stub_MatcherCollection
     */
    protected $collection;

    /**
     * @var PHPUnit_Framework_MockObject_Matcher
     */
    protected $matcher;

    /**
     * @var string[]
     */
    private $configurableMethods = [];

    /**
     * @param PHPUnit_Framework_MockObject_Stub_MatcherCollection $collection
     * @param PHPUnit_Framework_MockObject_Matcher_Invocation     $invocationMatcher
     * @param array                                               $configurableMethods
     */
    public function __construct(PHPUnit_Framework_MockObject_Stub_MatcherCollection $collection, PHPUnit_Framework_MockObject_Matcher_Invocation $invocationMatcher, array $configurableMethods)
    {
        $this->collection = $collection;
        $this->matcher    = new PHPUnit_Framework_MockObject_Matcher(
            $invocationMatcher
        );

        $this->collection->addMatcher($this->matcher);

        $this->configurableMethods = $configurableMethods;
    }

    /**
     * @return PHPUnit_Framework_MockObject_Matcher
     */
    public function getMatcher()
    {
        return $this->matcher;
    }

    /**
     * @param mixed $id
     *
     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
     */
    public function id($id)
    {
        $this->collection->registerId($id, $this);

        return $this;
    }

    /**
     * @param PHPUnit_Framework_MockObject_Stub $stub
     *
     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
     */
    public function will(PHPUnit_Framework_MockObject_Stub $stub)
    {
        $this->matcher->stub = $stub;

        return $this;
    }

    /**
     * @param mixed $value
     * @param mixed $nextValues, ...
     *
     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
     */
    public function willReturn($value, ...$nextValues)
    {
        $stub = count($nextValues) === 0 ?
            new PHPUnit_Framework_MockObject_Stub_Return($value) :
            new PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls(
               array_merge([$value], $nextValues)
            );

        return $this->will($stub);
    }

    /**
     * @param mixed $reference
     *
     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
     */
    public function willReturnReference(&$reference)
    {
        $stub = new PHPUnit_Framework_MockObject_Stub_ReturnReference($reference);

        return $this->will($stub);
    }

    /**
     * @param array $valueMap
     *
     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
     */
    public function willReturnMap(array $valueMap)
    {
        $stub = new PHPUnit_Framework_MockObject_Stub_ReturnValueMap(
            $valueMap
        );

        return $this->will($stub);
    }

    /**
     * @param mixed $argumentIndex
     *
     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
     */
    public function willReturnArgument($argumentIndex)
    {
        $stub = new PHPUnit_Framework_MockObject_Stub_ReturnArgument(
            $argumentIndex
        );

        return $this->will($stub);
    }

    /**
     * @param callable $callback
     *
     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
     */
    public function willReturnCallback($callback)
    {
        $stub = new PHPUnit_Framework_MockObject_Stub_ReturnCallback(
            $callback
        );

        return $this->will($stub);
    }

    /**
     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
     */
    public function willReturnSelf()
    {
        $stub = new PHPUnit_Framework_MockObject_Stub_ReturnSelf;

        return $this->will($stub);
    }

    /**
     * @param mixed $values, ...
     *
     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
     */
    public function willReturnOnConsecutiveCalls(...$values)
    {
        $stub = new PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls($values);

        return $this->will($stub);
    }

    /**
     * @param Exception $exception
     *
     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
     */
    public function willThrowException(Exception $exception)
    {
        $stub = new PHPUnit_Framework_MockObject_Stub_Exception($exception);

        return $this->will($stub);
    }

    /**
     * @param mixed $id
     *
     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
     */
    public function after($id)
    {
        $this->matcher->afterMatchBuilderId = $id;

        return $this;
    }

    /**
     * Validate that a parameters matcher can be defined, throw exceptions otherwise.
     *
     * @throws PHPUnit_Framework_MockObject_RuntimeException
     */
    private function canDefineParameters()
    {
        if ($this->matcher->methodNameMatcher === null) {
            throw new PHPUnit_Framework_MockObject_RuntimeException(
                'Method name matcher is not defined, cannot define parameter ' .
                'matcher without one'
            );
        }

        if ($this->matcher->parametersMatcher !== null) {
            throw new PHPUnit_Framework_MockObject_RuntimeException(
                'Parameter matcher is already defined, cannot redefine'
            );
        }
    }

    /**
     * @param  array ...$arguments
     *
     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
     */
    public function with(...$arguments)
    {
        $this->canDefineParameters();

        $this->matcher->parametersMatcher = new PHPUnit_Framework_MockObject_Matcher_Parameters($arguments);

        return $this;
    }

    /**
     * @param  array ...$arguments
     *
     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
     */
    public function withConsecutive(...$arguments)
    {
        $this->canDefineParameters();

        $this->matcher->parametersMatcher = new PHPUnit_Framework_MockObject_Matcher_ConsecutiveParameters($arguments);

        return $this;
    }

    /**
     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
     */
    public function withAnyParameters()
    {
        $this->canDefineParameters();

        $this->matcher->parametersMatcher = new PHPUnit_Framework_MockObject_Matcher_AnyParameters;

        return $this;
    }

    /**
     * @param PHPUnit_Framework_Constraint|string $constraint
     *
     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
     */
    public function method($constraint)
    {
        if ($this->matcher->methodNameMatcher !== null) {
            throw new PHPUnit_Framework_MockObject_RuntimeException(
                'Method name matcher is already defined, cannot redefine'
            );
        }

        if (is_string($constraint) && !in_array(strtolower($constraint), $this->configurableMethods)) {
            throw new PHPUnit_Framework_MockObject_RuntimeException(
                sprintf(
                    'Trying to configure method "%s" which cannot be configured because it does not exist, has not been specified, is final, or is static',
                    $constraint
                )
            );
        }

        $this->matcher->methodNameMatcher = new PHPUnit_Framework_MockObject_Matcher_MethodName($constraint);

        return $this;
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Builder interface for invocation order matches.
 *
 * @since Interface available since Release 1.0.0
 */
interface PHPUnit_Framework_MockObject_Builder_Match extends PHPUnit_Framework_MockObject_Builder_Stub
{
    /**
     * Defines the expectation which must occur before the current is valid.
     *
     * @param string $id The identification of the expectation that should
     *                   occur before this one.
     *
     * @return PHPUnit_Framework_MockObject_Builder_Stub
     */
    public function after($id);
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Builder interface for matcher of method names.
 *
 * @since Interface available since Release 1.0.0
 */
interface PHPUnit_Framework_MockObject_Builder_MethodNameMatch extends PHPUnit_Framework_MockObject_Builder_ParametersMatch
{
    /**
     * Adds a new method name match and returns the parameter match object for
     * further matching possibilities.
     *
     * @param PHPUnit_Framework_Constraint $name Constraint for matching method, if a string is passed it will use the PHPUnit_Framework_Constraint_IsEqual
     *
     * @return PHPUnit_Framework_MockObject_Builder_ParametersMatch
     */
    public function method($name);
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Interface for builders which can register builders with a given identification.
 *
 * This interface relates to PHPUnit_Framework_MockObject_Builder_Identity.
 *
 * @since Interface available since Release 1.0.0
 */
interface PHPUnit_Framework_MockObject_Builder_Namespace
{
    /**
     * Looks up the match builder with identification $id and returns it.
     *
     * @param string $id The identification of the match builder
     *
     * @return PHPUnit_Framework_MockObject_Builder_Match
     */
    public function lookupId($id);

    /**
     * Registers the match builder $builder with the identification $id. The
     * builder can later be looked up using lookupId() to figure out if it
     * has been invoked.
     *
     * @param string                                     $id      The identification of the match builder
     * @param PHPUnit_Framework_MockObject_Builder_Match $builder The builder which is being registered
     */
    public function registerId($id, PHPUnit_Framework_MockObject_Builder_Match $builder);
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Builder interface for parameter matchers.
 *
 * @since Interface available since Release 1.0.0
 */
interface PHPUnit_Framework_MockObject_Builder_ParametersMatch extends PHPUnit_Framework_MockObject_Builder_Match
{
    /**
     * Sets the parameters to match for, each parameter to this funtion will
     * be part of match. To perform specific matches or constraints create a
     * new PHPUnit_Framework_Constraint and use it for the parameter.
     * If the parameter value is not a constraint it will use the
     * PHPUnit_Framework_Constraint_IsEqual for the value.
     *
     * Some examples:
     * <code>
     * // match first parameter with value 2
     * $b->with(2);
     * // match first parameter with value 'smock' and second identical to 42
     * $b->with('smock', new PHPUnit_Framework_Constraint_IsEqual(42));
     * </code>
     *
     * @return PHPUnit_Framework_MockObject_Builder_ParametersMatch
     */
    public function with(...$arguments);

    /**
     * Sets a matcher which allows any kind of parameters.
     *
     * Some examples:
     * <code>
     * // match any number of parameters
     * $b->withAnyParameters();
     * </code>
     *
     * @return PHPUnit_Framework_MockObject_Matcher_AnyParameters
     */
    public function withAnyParameters();
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Builder interface for stubs which are actions replacing an invocation.
 *
 * @since Interface available since Release 1.0.0
 */
interface PHPUnit_Framework_MockObject_Builder_Stub extends PHPUnit_Framework_MockObject_Builder_Identity
{
    /**
     * Stubs the matching method with the stub object $stub. Any invocations of
     * the matched method will now be handled by the stub instead.
     *
     * @param PHPUnit_Framework_MockObject_Stub $stub
     *
     * @return PHPUnit_Framework_MockObject_Builder_Identity
     */
    public function will(PHPUnit_Framework_MockObject_Stub $stub);
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * @since Class available since Release 2.0.6
 */
class PHPUnit_Framework_MockObject_BadMethodCallException extends BadMethodCallException implements PHPUnit_Framework_MockObject_Exception
{
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Interface for exceptions used by PHPUnit_MockObject.
 *
 * @since Interface available since Release 2.0.6
 */
interface PHPUnit_Framework_MockObject_Exception
{
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * @since Class available since Release 2.0.6
 */
class PHPUnit_Framework_MockObject_RuntimeException extends RuntimeException implements PHPUnit_Framework_MockObject_Exception
{
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Doctrine\Instantiator\Instantiator;
use Doctrine\Instantiator\Exception\InvalidArgumentException as InstantiatorInvalidArgumentException;
use Doctrine\Instantiator\Exception\UnexpectedValueException as InstantiatorUnexpectedValueException;

/**
 * Mock Object Code Generator
 *
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_Generator
{
    /**
     * @var array
     */
    private static $cache = [];

    /**
     * @var Text_Template[]
     */
    private static $templates = [];

    /**
     * @var array
     */
    private $legacyBlacklistedMethodNames = [
        '__CLASS__'       => true,
        '__DIR__'         => true,
        '__FILE__'        => true,
        '__FUNCTION__'    => true,
        '__LINE__'        => true,
        '__METHOD__'      => true,
        '__NAMESPACE__'   => true,
        '__TRAIT__'       => true,
        '__clone'         => true,
        '__halt_compiler' => true,
        'abstract'        => true,
        'and'             => true,
        'array'           => true,
        'as'              => true,
        'break'           => true,
        'callable'        => true,
        'case'            => true,
        'catch'           => true,
        'class'           => true,
        'clone'           => true,
        'const'           => true,
        'continue'        => true,
        'declare'         => true,
        'default'         => true,
        'die'             => true,
        'do'              => true,
        'echo'            => true,
        'else'            => true,
        'elseif'          => true,
        'empty'           => true,
        'enddeclare'      => true,
        'endfor'          => true,
        'endforeach'      => true,
        'endif'           => true,
        'endswitch'       => true,
        'endwhile'        => true,
        'eval'            => true,
        'exit'            => true,
        'expects'         => true,
        'extends'         => true,
        'final'           => true,
        'for'             => true,
        'foreach'         => true,
        'function'        => true,
        'global'          => true,
        'goto'            => true,
        'if'              => true,
        'implements'      => true,
        'include'         => true,
        'include_once'    => true,
        'instanceof'      => true,
        'insteadof'       => true,
        'interface'       => true,
        'isset'           => true,
        'list'            => true,
        'namespace'       => true,
        'new'             => true,
        'or'              => true,
        'print'           => true,
        'private'         => true,
        'protected'       => true,
        'public'          => true,
        'require'         => true,
        'require_once'    => true,
        'return'          => true,
        'static'          => true,
        'switch'          => true,
        'throw'           => true,
        'trait'           => true,
        'try'             => true,
        'unset'           => true,
        'use'             => true,
        'var'             => true,
        'while'           => true,
        'xor'             => true
    ];

    /**
     * @var array
     */
    private $blacklistedMethodNames = [
        '__CLASS__'       => true,
        '__DIR__'         => true,
        '__FILE__'        => true,
        '__FUNCTION__'    => true,
        '__LINE__'        => true,
        '__METHOD__'      => true,
        '__NAMESPACE__'   => true,
        '__TRAIT__'       => true,
        '__clone'         => true,
        '__halt_compiler' => true,
    ];

    /**
     * Returns a mock object for the specified class.
     *
     * @param array|string $type
     * @param array        $methods
     * @param array        $arguments
     * @param string       $mockClassName
     * @param bool         $callOriginalConstructor
     * @param bool         $callOriginalClone
     * @param bool         $callAutoload
     * @param bool         $cloneArguments
     * @param bool         $callOriginalMethods
     * @param object       $proxyTarget
     * @param bool         $allowMockingUnknownTypes
     *
     * @return PHPUnit_Framework_MockObject_MockObject
     *
     * @throws InvalidArgumentException
     * @throws PHPUnit_Framework_Exception
     * @throws PHPUnit_Framework_MockObject_RuntimeException
     *
     * @since  Method available since Release 1.0.0
     */
    public function getMock($type, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = true, $callOriginalMethods = false, $proxyTarget = null, $allowMockingUnknownTypes = true)
    {
        if (!is_array($type) && !is_string($type)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'array or string');
        }

        if (!is_string($mockClassName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(4, 'string');
        }

        if (!is_array($methods) && !is_null($methods)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'array', $methods);
        }

        if ($type === 'Traversable' || $type === '\\Traversable') {
            $type = 'Iterator';
        }

        if (is_array($type)) {
            $type = array_unique(
                array_map(
                    function ($type) {
                        if ($type === 'Traversable' ||
                            $type === '\\Traversable' ||
                            $type === '\\Iterator') {
                            return 'Iterator';
                        }

                        return $type;
                    },
                    $type
                )
            );
        }

        if (!$allowMockingUnknownTypes) {
            if (is_array($type)) {
                foreach ($type as $_type) {
                    if (!class_exists($_type, $callAutoload) &&
                        !interface_exists($_type, $callAutoload)) {
                        throw new PHPUnit_Framework_MockObject_RuntimeException(
                            sprintf(
                                'Cannot stub or mock class or interface "%s" which does not exist',
                                $_type
                            )
                        );
                    }
                }
            } else {
                if (!class_exists($type, $callAutoload) &&
                    !interface_exists($type, $callAutoload)
                ) {
                    throw new PHPUnit_Framework_MockObject_RuntimeException(
                        sprintf(
                            'Cannot stub or mock class or interface "%s" which does not exist',
                            $type
                        )
                    );
                }
            }
        }

        if (null !== $methods) {
            foreach ($methods as $method) {
                if (!preg_match('~[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*~', $method)) {
                    throw new PHPUnit_Framework_MockObject_RuntimeException(
                        sprintf(
                            'Cannot stub or mock method with invalid name "%s"',
                            $method
                        )
                    );
                }
            }

            if ($methods != array_unique($methods)) {
                throw new PHPUnit_Framework_MockObject_RuntimeException(
                    sprintf(
                        'Cannot stub or mock using a method list that contains duplicates: "%s" (duplicate: "%s")',
                        implode(', ', $methods),
                        implode(', ', array_unique(array_diff_assoc($methods, array_unique($methods))))
                    )
                );
            }
        }

        if ($mockClassName != '' && class_exists($mockClassName, false)) {
            $reflect = new ReflectionClass($mockClassName);

            if (!$reflect->implementsInterface('PHPUnit_Framework_MockObject_MockObject')) {
                throw new PHPUnit_Framework_MockObject_RuntimeException(
                    sprintf(
                        'Class "%s" already exists.',
                        $mockClassName
                    )
                );
            }
        }

        if ($callOriginalConstructor === false && $callOriginalMethods === true) {
            throw new PHPUnit_Framework_MockObject_RuntimeException(
                'Proxying to original methods requires invoking the original constructor'
            );
        }

        $mock = $this->generate(
            $type,
            $methods,
            $mockClassName,
            $callOriginalClone,
            $callAutoload,
            $cloneArguments,
            $callOriginalMethods
        );

        return $this->getObject(
            $mock['code'],
            $mock['mockClassName'],
            $type,
            $callOriginalConstructor,
            $callAutoload,
            $arguments,
            $callOriginalMethods,
            $proxyTarget
        );
    }

    /**
     * @param string       $code
     * @param string       $className
     * @param array|string $type
     * @param bool         $callOriginalConstructor
     * @param bool         $callAutoload
     * @param array        $arguments
     * @param bool         $callOriginalMethods
     * @param object       $proxyTarget
     *
     * @return object
     */
    private function getObject($code, $className, $type = '', $callOriginalConstructor = false, $callAutoload = false, array $arguments = [], $callOriginalMethods = false, $proxyTarget = null)
    {
        $this->evalClass($code, $className);

        if ($callOriginalConstructor &&
            is_string($type) &&
            !interface_exists($type, $callAutoload)) {
            if (count($arguments) == 0) {
                $object = new $className;
            } else {
                $class  = new ReflectionClass($className);
                $object = $class->newInstanceArgs($arguments);
            }
        } else {
            try {
                $instantiator = new Instantiator;
                $object       = $instantiator->instantiate($className);
            } catch (InstantiatorUnexpectedValueException $exception) {
                if ($exception->getPrevious()) {
                    $exception = $exception->getPrevious();
                }

                throw new PHPUnit_Framework_MockObject_RuntimeException(
                    $exception->getMessage()
                );
            } catch (InstantiatorInvalidArgumentException $exception) {
                throw new PHPUnit_Framework_MockObject_RuntimeException(
                    $exception->getMessage()
                );
            }
        }

        if ($callOriginalMethods) {
            if (!is_object($proxyTarget)) {
                if (count($arguments) == 0) {
                    $proxyTarget = new $type;
                } else {
                    $class       = new ReflectionClass($type);
                    $proxyTarget = $class->newInstanceArgs($arguments);
                }
            }

            $object->__phpunit_setOriginalObject($proxyTarget);
        }

        return $object;
    }

    /**
     * @param string $code
     * @param string $className
     */
    private function evalClass($code, $className)
    {
        if (!class_exists($className, false)) {
            eval($code);
        }
    }

    /**
     * Returns a mock object for the specified abstract class with all abstract
     * methods of the class mocked. Concrete methods to mock can be specified with
     * the last parameter
     *
     * @param string $originalClassName
     * @param array  $arguments
     * @param string $mockClassName
     * @param bool   $callOriginalConstructor
     * @param bool   $callOriginalClone
     * @param bool   $callAutoload
     * @param array  $mockedMethods
     * @param bool   $cloneArguments
     *
     * @return PHPUnit_Framework_MockObject_MockObject
     *
     * @throws PHPUnit_Framework_MockObject_RuntimeException
     * @throws PHPUnit_Framework_Exception
     *
     * @since  Method available since Release 1.0.0
     */
    public function getMockForAbstractClass($originalClassName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = true)
    {
        if (!is_string($originalClassName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!is_string($mockClassName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'string');
        }

        if (class_exists($originalClassName, $callAutoload) ||
            interface_exists($originalClassName, $callAutoload)) {
            $reflector = new ReflectionClass($originalClassName);
            $methods   = $mockedMethods;

            foreach ($reflector->getMethods() as $method) {
                if ($method->isAbstract() && !in_array($method->getName(), $methods)) {
                    $methods[] = $method->getName();
                }
            }

            if (empty($methods)) {
                $methods = null;
            }

            return $this->getMock(
                $originalClassName,
                $methods,
                $arguments,
                $mockClassName,
                $callOriginalConstructor,
                $callOriginalClone,
                $callAutoload,
                $cloneArguments
            );
        } else {
            throw new PHPUnit_Framework_MockObject_RuntimeException(
                sprintf('Class "%s" does not exist.', $originalClassName)
            );
        }
    }

    /**
     * Returns a mock object for the specified trait with all abstract methods
     * of the trait mocked. Concrete methods to mock can be specified with the
     * `$mockedMethods` parameter.
     *
     * @param string $traitName
     * @param array  $arguments
     * @param string $mockClassName
     * @param bool   $callOriginalConstructor
     * @param bool   $callOriginalClone
     * @param bool   $callAutoload
     * @param array  $mockedMethods
     * @param bool   $cloneArguments
     *
     * @return PHPUnit_Framework_MockObject_MockObject
     *
     * @throws PHPUnit_Framework_MockObject_RuntimeException
     * @throws PHPUnit_Framework_Exception
     *
     * @since  Method available since Release 1.2.3
     */
    public function getMockForTrait($traitName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = true)
    {
        if (!is_string($traitName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!is_string($mockClassName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'string');
        }

        if (!trait_exists($traitName, $callAutoload)) {
            throw new PHPUnit_Framework_MockObject_RuntimeException(
                sprintf(
                    'Trait "%s" does not exist.',
                    $traitName
                )
            );
        }

        $className = $this->generateClassName(
            $traitName,
            '',
            'Trait_'
        );

        $templateDir   = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR;
        $classTemplate = $this->getTemplate($templateDir . 'trait_class.tpl');

        $classTemplate->setVar(
            [
                'prologue'   => 'abstract ',
                'class_name' => $className['className'],
                'trait_name' => $traitName
            ]
        );

        $this->evalClass(
            $classTemplate->render(),
            $className['className']
        );

        return $this->getMockForAbstractClass($className['className'], $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments);
    }

    /**
     * Returns an object for the specified trait.
     *
     * @param string $traitName
     * @param array  $arguments
     * @param string $traitClassName
     * @param bool   $callOriginalConstructor
     * @param bool   $callOriginalClone
     * @param bool   $callAutoload
     *
     * @return object
     *
     * @throws PHPUnit_Framework_MockObject_RuntimeException
     * @throws PHPUnit_Framework_Exception
     *
     * @since  Method available since Release 1.1.0
     */
    public function getObjectForTrait($traitName, array $arguments = [], $traitClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true)
    {
        if (!is_string($traitName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
        }

        if (!is_string($traitClassName)) {
            throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'string');
        }

        if (!trait_exists($traitName, $callAutoload)) {
            throw new PHPUnit_Framework_MockObject_RuntimeException(
                sprintf(
                    'Trait "%s" does not exist.',
                    $traitName
                )
            );
        }

        $className = $this->generateClassName(
            $traitName,
            $traitClassName,
            'Trait_'
        );

        $templateDir   = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR;
        $classTemplate = $this->getTemplate($templateDir . 'trait_class.tpl');

        $classTemplate->setVar(
            [
                'prologue'   => '',
                'class_name' => $className['className'],
                'trait_name' => $traitName
            ]
        );

        return $this->getObject(
            $classTemplate->render(),
            $className['className']
        );
    }

    /**
     * @param array|string $type
     * @param array        $methods
     * @param string       $mockClassName
     * @param bool         $callOriginalClone
     * @param bool         $callAutoload
     * @param bool         $cloneArguments
     * @param bool         $callOriginalMethods
     *
     * @return array
     */
    public function generate($type, array $methods = null, $mockClassName = '', $callOriginalClone = true, $callAutoload = true, $cloneArguments = true, $callOriginalMethods = false)
    {
        if (is_array($type)) {
            sort($type);
        }

        if ($mockClassName == '') {
            $key = md5(
                is_array($type) ? implode('_', $type) : $type .
                serialize($methods) .
                serialize($callOriginalClone) .
                serialize($cloneArguments) .
                serialize($callOriginalMethods)
            );

            if (isset(self::$cache[$key])) {
                return self::$cache[$key];
            }
        }

        $mock = $this->generateMock(
            $type,
            $methods,
            $mockClassName,
            $callOriginalClone,
            $callAutoload,
            $cloneArguments,
            $callOriginalMethods
        );

        if (isset($key)) {
            self::$cache[$key] = $mock;
        }

        return $mock;
    }

    /**
     * @param string $wsdlFile
     * @param string $className
     * @param array  $methods
     * @param array  $options
     *
     * @return string
     *
     * @throws PHPUnit_Framework_MockObject_RuntimeException
     */
    public function generateClassFromWsdl($wsdlFile, $className, array $methods = [], array $options = [])
    {
        if (!extension_loaded('soap')) {
            throw new PHPUnit_Framework_MockObject_RuntimeException(
                'The SOAP extension is required to generate a mock object from WSDL.'
            );
        }

        $options  = array_merge($options, ['cache_wsdl' => WSDL_CACHE_NONE]);
        $client   = new SoapClient($wsdlFile, $options);
        $_methods = array_unique($client->__getFunctions());
        unset($client);

        sort($_methods);

        $templateDir    = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR;
        $methodTemplate = $this->getTemplate($templateDir . 'wsdl_method.tpl');
        $methodsBuffer  = '';

        foreach ($_methods as $method) {
            $nameStart = strpos($method, ' ') + 1;
            $nameEnd   = strpos($method, '(');
            $name      = substr($method, $nameStart, $nameEnd - $nameStart);

            if (empty($methods) || in_array($name, $methods)) {
                $args    = explode(
                    ',',
                    substr(
                        $method,
                        $nameEnd + 1,
                        strpos($method, ')') - $nameEnd - 1
                    )
                );
                $numArgs = count($args);

                for ($i = 0; $i < $numArgs; $i++) {
                    $args[$i] = substr($args[$i], strpos($args[$i], '$'));
                }

                $methodTemplate->setVar(
                    [
                        'method_name' => $name,
                        'arguments'   => implode(', ', $args)
                    ]
                );

                $methodsBuffer .= $methodTemplate->render();
            }
        }

        $optionsBuffer = 'array(';

        foreach ($options as $key => $value) {
            $optionsBuffer .= $key . ' => ' . $value;
        }

        $optionsBuffer .= ')';

        $classTemplate = $this->getTemplate($templateDir . 'wsdl_class.tpl');
        $namespace     = '';

        if (strpos($className, '\\') !== false) {
            $parts     = explode('\\', $className);
            $className = array_pop($parts);
            $namespace = 'namespace ' . implode('\\', $parts) . ';' . "\n\n";
        }

        $classTemplate->setVar(
            [
                'namespace'  => $namespace,
                'class_name' => $className,
                'wsdl'       => $wsdlFile,
                'options'    => $optionsBuffer,
                'methods'    => $methodsBuffer
            ]
        );

        return $classTemplate->render();
    }

    /**
     * @param array|string $type
     * @param array|null   $methods
     * @param string       $mockClassName
     * @param bool         $callOriginalClone
     * @param bool         $callAutoload
     * @param bool         $cloneArguments
     * @param bool         $callOriginalMethods
     *
     * @return array
     *
     * @throws PHPUnit_Framework_MockObject_RuntimeException
     */
    private function generateMock($type, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods)
    {
        $methodReflections   = [];
        $templateDir         = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR;
        $classTemplate       = $this->getTemplate($templateDir . 'mocked_class.tpl');

        $additionalInterfaces = [];
        $cloneTemplate        = '';
        $isClass              = false;
        $isInterface          = false;
        $isMultipleInterfaces = false;

        if (is_array($type)) {
            foreach ($type as $_type) {
                if (!interface_exists($_type, $callAutoload)) {
                    throw new PHPUnit_Framework_MockObject_RuntimeException(
                        sprintf(
                            'Interface "%s" does not exist.',
                            $_type
                        )
                    );
                }

                $isMultipleInterfaces = true;

                $additionalInterfaces[] = $_type;
                $typeClass              = new ReflectionClass($this->generateClassName(
                    $_type,
                    $mockClassName,
                    'Mock_'
                    )['fullClassName']
                );

                foreach ($this->getClassMethods($_type) as $method) {
                    if (in_array($method, $methods)) {
                        throw new PHPUnit_Framework_MockObject_RuntimeException(
                            sprintf(
                                'Duplicate method "%s" not allowed.',
                                $method
                            )
                        );
                    }

                    $methodReflections[$method] = $typeClass->getMethod($method);
                    $methods[]                  = $method;
                }
            }
        }

        $mockClassName = $this->generateClassName(
            $type,
            $mockClassName,
            'Mock_'
        );

        if (class_exists($mockClassName['fullClassName'], $callAutoload)) {
            $isClass = true;
        } elseif (interface_exists($mockClassName['fullClassName'], $callAutoload)) {
            $isInterface = true;
        }

        if (!$isClass && !$isInterface) {
            $prologue = 'class ' . $mockClassName['originalClassName'] . "\n{\n}\n\n";

            if (!empty($mockClassName['namespaceName'])) {
                $prologue = 'namespace ' . $mockClassName['namespaceName'] .
                            " {\n\n" . $prologue . "}\n\n" .
                            "namespace {\n\n";

                $epilogue = "\n\n}";
            }

            $cloneTemplate = $this->getTemplate($templateDir . 'mocked_clone.tpl');
        } else {
            $class = new ReflectionClass($mockClassName['fullClassName']);

            if ($class->isFinal()) {
                throw new PHPUnit_Framework_MockObject_RuntimeException(
                    sprintf(
                        'Class "%s" is declared "final" and cannot be mocked.',
                        $mockClassName['fullClassName']
                    )
                );
            }

            if ($class->hasMethod('__clone')) {
                $cloneMethod = $class->getMethod('__clone');

                if (!$cloneMethod->isFinal()) {
                    if ($callOriginalClone && !$isInterface) {
                        $cloneTemplate = $this->getTemplate($templateDir . 'unmocked_clone.tpl');
                    } else {
                        $cloneTemplate = $this->getTemplate($templateDir . 'mocked_clone.tpl');
                    }
                }
            } else {
                $cloneTemplate = $this->getTemplate($templateDir . 'mocked_clone.tpl');
            }
        }

        if (is_object($cloneTemplate)) {
            $cloneTemplate = $cloneTemplate->render();
        }

        if (is_array($methods) && empty($methods) &&
            ($isClass || $isInterface)) {
            $methods = $this->getClassMethods($mockClassName['fullClassName']);
        }

        if (!is_array($methods)) {
            $methods = [];
        }

        $mockedMethods = '';
        $configurable  = [];

        foreach ($methods as $methodName) {
            if ($methodName != '__construct' && $methodName != '__clone') {
                $configurable[] = strtolower($methodName);
            }
        }

        if (isset($class)) {
            // https://github.com/sebastianbergmann/phpunit-mock-objects/issues/103
            if ($isInterface && $class->implementsInterface('Traversable') &&
                !$class->implementsInterface('Iterator') &&
                !$class->implementsInterface('IteratorAggregate')) {
                $additionalInterfaces[] = 'Iterator';
                $methods                = array_merge($methods, $this->getClassMethods('Iterator'));
            }

            foreach ($methods as $methodName) {
                try {
                    $method = $class->getMethod($methodName);

                    if ($this->canMockMethod($method)) {
                        $mockedMethods .= $this->generateMockedMethodDefinitionFromExisting(
                            $templateDir,
                            $method,
                            $cloneArguments,
                            $callOriginalMethods
                        );
                    }
                } catch (ReflectionException $e) {
                    $mockedMethods .= $this->generateMockedMethodDefinition(
                        $templateDir,
                        $mockClassName['fullClassName'],
                        $methodName,
                        $cloneArguments
                    );
                }
            }
        } elseif ($isMultipleInterfaces) {
            foreach ($methods as $methodName) {
                if ($this->canMockMethod($methodReflections[$methodName])) {
                    $mockedMethods .= $this->generateMockedMethodDefinitionFromExisting(
                        $templateDir,
                        $methodReflections[$methodName],
                        $cloneArguments,
                        $callOriginalMethods
                    );
                }
            }
        } else {
            foreach ($methods as $methodName) {
                $mockedMethods .= $this->generateMockedMethodDefinition(
                    $templateDir,
                    $mockClassName['fullClassName'],
                    $methodName,
                    $cloneArguments
                );
            }
        }

        $method = '';

        if (!in_array('method', $methods) && (!isset($class) || !$class->hasMethod('method'))) {
            $methodTemplate = $this->getTemplate($templateDir . 'mocked_class_method.tpl');

            $method = $methodTemplate->render();
        }

        $classTemplate->setVar(
            [
                'prologue'          => isset($prologue) ? $prologue : '',
                'epilogue'          => isset($epilogue) ? $epilogue : '',
                'class_declaration' => $this->generateMockClassDeclaration(
                    $mockClassName,
                    $isInterface,
                    $additionalInterfaces
                ),
                'clone'             => $cloneTemplate,
                'mock_class_name'   => $mockClassName['className'],
                'mocked_methods'    => $mockedMethods,
                'method'            => $method,
                'configurable'      => '[' . implode(', ', array_map(function ($m) { return '\'' . $m . '\'';}, $configurable)) . ']'
            ]
        );

        return [
          'code'          => $classTemplate->render(),
          'mockClassName' => $mockClassName['className']
        ];
    }

    /**
     * @param array|string $type
     * @param string       $className
     * @param string       $prefix
     *
     * @return array
     */
    private function generateClassName($type, $className, $prefix)
    {
        if (is_array($type)) {
            $type = implode('_', $type);
        }

        if ($type[0] == '\\') {
            $type = substr($type, 1);
        }

        $classNameParts = explode('\\', $type);

        if (count($classNameParts) > 1) {
            $type          = array_pop($classNameParts);
            $namespaceName = implode('\\', $classNameParts);
            $fullClassName = $namespaceName . '\\' . $type;
        } else {
            $namespaceName = '';
            $fullClassName = $type;
        }

        if ($className == '') {
            do {
                $className = $prefix . $type . '_' .
                             substr(md5(microtime()), 0, 8);
            } while (class_exists($className, false));
        }

        return [
          'className'         => $className,
          'originalClassName' => $type,
          'fullClassName'     => $fullClassName,
          'namespaceName'     => $namespaceName
        ];
    }

    /**
     * @param array $mockClassName
     * @param bool  $isInterface
     * @param array $additionalInterfaces
     *
     * @return array
     */
    private function generateMockClassDeclaration(array $mockClassName, $isInterface, array $additionalInterfaces = [])
    {
        $buffer = 'class ';

        $additionalInterfaces[] = 'PHPUnit_Framework_MockObject_MockObject';
        $interfaces             = implode(', ', $additionalInterfaces);

        if ($isInterface) {
            $buffer .= sprintf(
                '%s implements %s',
                $mockClassName['className'],
                $interfaces
            );

            if (!in_array($mockClassName['originalClassName'], $additionalInterfaces)) {
                $buffer .= ', ';

                if (!empty($mockClassName['namespaceName'])) {
                    $buffer .= $mockClassName['namespaceName'] . '\\';
                }

                $buffer .= $mockClassName['originalClassName'];
            }
        } else {
            $buffer .= sprintf(
                '%s extends %s%s implements %s',
                $mockClassName['className'],
                !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '',
                $mockClassName['originalClassName'],
                $interfaces
            );
        }

        return $buffer;
    }

    /**
     * @param string           $templateDir
     * @param ReflectionMethod $method
     * @param bool             $cloneArguments
     * @param bool             $callOriginalMethods
     *
     * @return string
     */
    private function generateMockedMethodDefinitionFromExisting($templateDir, ReflectionMethod $method, $cloneArguments, $callOriginalMethods)
    {
        if ($method->isPrivate()) {
            $modifier = 'private';
        } elseif ($method->isProtected()) {
            $modifier = 'protected';
        } else {
            $modifier = 'public';
        }

        if ($method->isStatic()) {
            $modifier .= ' static';
        }

        if ($method->returnsReference()) {
            $reference = '&';
        } else {
            $reference = '';
        }

        if ($this->hasReturnType($method)) {
            $returnType = (string) $method->getReturnType();
        } else {
            $returnType = '';
        }

        if (preg_match('#\*[ \t]*+@deprecated[ \t]*+(.*?)\r?+\n[ \t]*+\*(?:[ \t]*+@|/$)#s', $method->getDocComment(), $deprecation)) {
            $deprecation = trim(preg_replace('#[ \t]*\r?\n[ \t]*+\*[ \t]*+#', ' ', $deprecation[1]));
        } else {
            $deprecation = false;
        }

        return $this->generateMockedMethodDefinition(
            $templateDir,
            $method->getDeclaringClass()->getName(),
            $method->getName(),
            $cloneArguments,
            $modifier,
            $this->getMethodParameters($method),
            $this->getMethodParameters($method, true),
            $returnType,
            $reference,
            $callOriginalMethods,
            $method->isStatic(),
            $deprecation,
            $this->allowsReturnNull($method)
        );
    }

    /**
     * @param string       $templateDir
     * @param string       $className
     * @param string       $methodName
     * @param bool         $cloneArguments
     * @param string       $modifier
     * @param string       $arguments_decl
     * @param string       $arguments_call
     * @param string       $return_type
     * @param string       $reference
     * @param bool         $callOriginalMethods
     * @param bool         $static
     * @param string|false $deprecation
     * @param bool         $allowsReturnNull
     *
     * @return string
     */
    private function generateMockedMethodDefinition($templateDir, $className, $methodName, $cloneArguments = true, $modifier = 'public', $arguments_decl = '', $arguments_call = '', $return_type = '', $reference = '', $callOriginalMethods = false, $static = false, $deprecation = false, $allowsReturnNull = false)
    {
        if ($static) {
            $templateFile = 'mocked_static_method.tpl';
        } else {
            if ($return_type === 'void') {
                $templateFile = sprintf(
                    '%s_method_void.tpl',
                    $callOriginalMethods ? 'proxied' : 'mocked'
                );
            } else {
                $templateFile = sprintf(
                    '%s_method.tpl',
                    $callOriginalMethods ? 'proxied' : 'mocked'
                );
            }
        }

        // Mocked interfaces returning 'self' must explicitly declare the
        // interface name as the return type. See
        // https://bugs.php.net/bug.php?id=70722
        if ($return_type === 'self') {
            $return_type = $className;
        }

        if (false !== $deprecation) {
            $deprecation         = "The $className::$methodName method is deprecated ($deprecation).";
            $deprecationTemplate = $this->getTemplate($templateDir . 'deprecation.tpl');

            $deprecationTemplate->setVar(
                [
                    'deprecation' => var_export($deprecation, true),
                ]
            );

            $deprecation = $deprecationTemplate->render();
        }

        $template = $this->getTemplate($templateDir . $templateFile);

        $template->setVar(
            [
                'arguments_decl'  => $arguments_decl,
                'arguments_call'  => $arguments_call,
                'return_delim'    => $return_type ? ': ' : '',
                'return_type'     => $allowsReturnNull ? '?' . $return_type : $return_type,
                'arguments_count' => !empty($arguments_call) ? count(explode(',', $arguments_call)) : 0,
                'class_name'      => $className,
                'method_name'     => $methodName,
                'modifier'        => $modifier,
                'reference'       => $reference,
                'clone_arguments' => $cloneArguments ? 'true' : 'false',
                'deprecation'     => $deprecation
            ]
        );

        return $template->render();
    }

    /**
     * @param ReflectionMethod $method
     *
     * @return bool
     */
    private function canMockMethod(ReflectionMethod $method)
    {
        if ($method->isConstructor() ||
            $method->isFinal() ||
            $method->isPrivate() ||
            $this->isMethodNameBlacklisted($method->getName())) {
            return false;
        }

        return true;
    }

    /**
     * Returns whether i method name is blacklisted
     *
     * Since PHP 7 the only names that are still reserved for method names are the ones that start with an underscore
     *
     * @param string $name
     *
     * @return bool
     */
    private function isMethodNameBlacklisted($name)
    {
        if (PHP_MAJOR_VERSION < 7 && isset($this->legacyBlacklistedMethodNames[$name])) {
            return true;
        }

        if (PHP_MAJOR_VERSION >= 7 && isset($this->blacklistedMethodNames[$name])) {
            return true;
        }

        return false;
    }

    /**
     * Returns the parameters of a function or method.
     *
     * @param ReflectionMethod $method
     * @param bool             $forCall
     *
     * @return string
     *
     * @throws PHPUnit_Framework_MockObject_RuntimeException
     *
     * @since  Method available since Release 2.0.0
     */
    private function getMethodParameters(ReflectionMethod $method, $forCall = false)
    {
        $parameters = [];

        foreach ($method->getParameters() as $i => $parameter) {
            $name = '$' . $parameter->getName();

            /* Note: PHP extensions may use empty names for reference arguments
             * or "..." for methods taking a variable number of arguments.
             */
            if ($name === '$' || $name === '$...') {
                $name = '$arg' . $i;
            }

            if ($this->isVariadic($parameter)) {
                if ($forCall) {
                    continue;
                } else {
                    $name = '...' . $name;
                }
            }

            $nullable        = '';
            $default         = '';
            $reference       = '';
            $typeDeclaration = '';

            if (!$forCall) {
                if ($this->hasType($parameter) && (string) $parameter->getType() !== 'self') {
                    if (version_compare(PHP_VERSION, '7.1', '>=') && $parameter->allowsNull() && !$parameter->isVariadic()) {
                        $nullable = '?';
                    }

                    $typeDeclaration = (string) $parameter->getType() . ' ';
                } elseif ($parameter->isArray()) {
                    $typeDeclaration = 'array ';
                } elseif ($parameter->isCallable()) {
                    $typeDeclaration = 'callable ';
                } else {
                    try {
                        $class = $parameter->getClass();
                    } catch (ReflectionException $e) {
                        throw new PHPUnit_Framework_MockObject_RuntimeException(
                            sprintf(
                                'Cannot mock %s::%s() because a class or ' .
                                'interface used in the signature is not loaded',
                                $method->getDeclaringClass()->getName(),
                                $method->getName()
                            ),
                            0,
                            $e
                        );
                    }

                    if ($class !== null) {
                        $typeDeclaration = $class->getName() . ' ';
                    }
                }

                if (!$this->isVariadic($parameter)) {
                    if ($parameter->isDefaultValueAvailable()) {
                        $value   = $parameter->getDefaultValue();
                        $default = ' = ' . var_export($value, true);
                    } elseif ($parameter->isOptional()) {
                        $default = ' = null';
                    }
                }
            }

            if ($parameter->isPassedByReference()) {
                $reference = '&';
            }

            $parameters[] = $nullable . $typeDeclaration . $reference . $name . $default;
        }

        return implode(', ', $parameters);
    }

    /**
     * @param ReflectionParameter $parameter
     *
     * @return bool
     *
     * @since  Method available since Release 2.2.1
     */
    private function isVariadic(ReflectionParameter $parameter)
    {
        return method_exists(ReflectionParameter::class, 'isVariadic') && $parameter->isVariadic();
    }

    /**
     * @param ReflectionParameter $parameter
     *
     * @return bool
     *
     * @since  Method available since Release 2.3.4
     */
    private function hasType(ReflectionParameter $parameter)
    {
        return method_exists(ReflectionParameter::class, 'hasType') && $parameter->hasType();
    }

    /**
     * @param ReflectionMethod $method
     *
     * @return bool
     */
    private function hasReturnType(ReflectionMethod $method)
    {
        return method_exists(ReflectionMethod::class, 'hasReturnType') && $method->hasReturnType();
    }

    /**
     * @param ReflectionMethod $method
     *
     * @return bool
     */
    private function allowsReturnNull(ReflectionMethod $method)
    {
        return method_exists(ReflectionMethod::class, 'getReturnType')
            && method_exists(ReflectionType::class, 'allowsNull')
            && $method->hasReturnType()
            && $method->getReturnType()->allowsNull();
    }

    /**
     * @param string $className
     *
     * @return array
     *
     * @since  Method available since Release 2.3.2
     */
    public function getClassMethods($className)
    {
        $class   = new ReflectionClass($className);
        $methods = [];

        foreach ($class->getMethods() as $method) {
            if ($method->isPublic() || $method->isAbstract()) {
                $methods[] = $method->getName();
            }
        }

        return $methods;
    }

    /**
     * @param string $filename
     *
     * @return Text_Template
     *
     * @since  Method available since Release 3.2.4
     */
    private function getTemplate($filename)
    {
        if (!isset(self::$templates[$filename])) {
            self::$templates[$filename] = new Text_Template($filename);
        }

        return self::$templates[$filename];
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Represents a non-static invocation.
 *
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_Invocation_Object extends PHPUnit_Framework_MockObject_Invocation_Static
{
    /**
     * @var object
     */
    public $object;

    /**
     * @param string $className
     * @param string $methodName
     * @param array  $parameters
     * @param string $returnType
     * @param object $object
     * @param bool   $cloneObjects
     */
    public function __construct($className, $methodName, array $parameters, $returnType, $object, $cloneObjects = false)
    {
        parent::__construct($className, $methodName, $parameters, $returnType, $cloneObjects);

        $this->object = $object;
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use SebastianBergmann\Exporter\Exporter;

/**
 * Represents a static invocation.
 *
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_Invocation_Static implements PHPUnit_Framework_MockObject_Invocation, PHPUnit_Framework_SelfDescribing
{
    /**
     * @var array
     */
    protected static $uncloneableExtensions = [
        'mysqli'    => true,
        'SQLite'    => true,
        'sqlite3'   => true,
        'tidy'      => true,
        'xmlwriter' => true,
        'xsl'       => true
    ];

    /**
     * @var array
     */
    protected static $uncloneableClasses = [
        'Closure',
        'COMPersistHelper',
        'IteratorIterator',
        'RecursiveIteratorIterator',
        'SplFileObject',
        'PDORow',
        'ZipArchive'
    ];

    /**
     * @var string
     */
    public $className;

    /**
     * @var string
     */
    public $methodName;

    /**
     * @var array
     */
    public $parameters;

    /**
     * @var string
     */
    public $returnType;

    /**
     * @var bool
     */
    public $returnTypeNullable = false;

    /**
     * @param string $className
     * @param string $methodName
     * @param array  $parameters
     * @param string $returnType
     * @param bool   $cloneObjects
     */
    public function __construct($className, $methodName, array $parameters, $returnType, $cloneObjects = false)
    {
        $this->className  = $className;
        $this->methodName = $methodName;
        $this->parameters = $parameters;

        if (strpos($returnType, '?') === 0) {
            $returnType               = substr($returnType, 1);
            $this->returnTypeNullable = true;
        }

        $this->returnType = $returnType;

        if (!$cloneObjects) {
            return;
        }

        foreach ($this->parameters as $key => $value) {
            if (is_object($value)) {
                $this->parameters[$key] = $this->cloneObject($value);
            }
        }
    }

    /**
     * @return string
     */
    public function toString()
    {
        $exporter = new Exporter;

        return sprintf(
            '%s::%s(%s)%s',
            $this->className,
            $this->methodName,
            implode(
                ', ',
                array_map(
                    [$exporter, 'shortenedExport'],
                    $this->parameters
                )
            ),
            $this->returnType ? sprintf(': %s', $this->returnType) : ''
        );
    }

    /**
     * @return mixed Mocked return value.
     */
    public function generateReturnValue()
    {
        switch ($this->returnType) {
            case '':       return;
            case 'string': return $this->returnTypeNullable ? null : '';
            case 'float':  return $this->returnTypeNullable ? null : 0.0;
            case 'int':    return $this->returnTypeNullable ? null : 0;
            case 'bool':   return $this->returnTypeNullable ? null : false;
            case 'array':  return $this->returnTypeNullable ? null : [];
            case 'void':   return;

            case 'callable':
            case 'Closure':
                return function () {};

            case 'Traversable':
            case 'Generator':
                $generator = function () { yield; };

                return $generator();

            default:
                if ($this->returnTypeNullable) {
                    return null;
                }

                $generator = new PHPUnit_Framework_MockObject_Generator;

                return $generator->getMock($this->returnType, [], [], '', false);
        }
    }

    /**
     * @param object $original
     *
     * @return object
     */
    protected function cloneObject($original)
    {
        $cloneable = null;
        $object    = new ReflectionObject($original);

        // Check the blacklist before asking PHP reflection to work around
        // https://bugs.php.net/bug.php?id=53967
        if ($object->isInternal() &&
            isset(self::$uncloneableExtensions[$object->getExtensionName()])) {
            $cloneable = false;
        }

        if ($cloneable === null) {
            foreach (self::$uncloneableClasses as $class) {
                if ($original instanceof $class) {
                    $cloneable = false;
                    break;
                }
            }
        }

        if ($cloneable === null && method_exists($object, 'isCloneable')) {
            $cloneable = $object->isCloneable();
        }

        if ($cloneable === null && $object->hasMethod('__clone')) {
            $method    = $object->getMethod('__clone');
            $cloneable = $method->isPublic();
        }

        if ($cloneable === null) {
            $cloneable = true;
        }

        if ($cloneable) {
            try {
                return clone $original;
            } catch (Exception $e) {
                return $original;
            }
        } else {
            return $original;
        }
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Interface for invocations.
 *
 * @since Interface available since Release 1.0.0
 */
interface PHPUnit_Framework_MockObject_Invocation
{
    /**
     * @return mixed Mocked return value.
     */
    public function generateReturnValue();
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Mocker for invocations which are sent from
 * PHPUnit_Framework_MockObject_MockObject objects.
 *
 * Keeps track of all expectations and stubs as well as registering
 * identifications for builders.
 *
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_InvocationMocker implements PHPUnit_Framework_MockObject_Stub_MatcherCollection, PHPUnit_Framework_MockObject_Invokable, PHPUnit_Framework_MockObject_Builder_Namespace
{
    /**
     * @var PHPUnit_Framework_MockObject_Matcher_Invocation[]
     */
    protected $matchers = [];

    /**
     * @var PHPUnit_Framework_MockObject_Builder_Match[]
     */
    protected $builderMap = [];

    /**
     * @var string[]
     */
    private $configurableMethods = [];

    /**
     * @param array $configurableMethods
     */
    public function __construct(array $configurableMethods)
    {
        $this->configurableMethods = $configurableMethods;
    }

    /**
     * @param PHPUnit_Framework_MockObject_Matcher_Invocation $matcher
     */
    public function addMatcher(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher)
    {
        $this->matchers[] = $matcher;
    }

    /**
     * @since Method available since Release 1.1.0
     */
    public function hasMatchers()
    {
        foreach ($this->matchers as $matcher) {
            if ($matcher->hasMatchers()) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param mixed $id
     *
     * @return bool|null
     */
    public function lookupId($id)
    {
        if (isset($this->builderMap[$id])) {
            return $this->builderMap[$id];
        }

        return;
    }

    /**
     * @param mixed                                      $id
     * @param PHPUnit_Framework_MockObject_Builder_Match $builder
     *
     * @throws PHPUnit_Framework_MockObject_RuntimeException
     */
    public function registerId($id, PHPUnit_Framework_MockObject_Builder_Match $builder)
    {
        if (isset($this->builderMap[$id])) {
            throw new PHPUnit_Framework_MockObject_RuntimeException(
                'Match builder with id <' . $id . '> is already registered.'
            );
        }

        $this->builderMap[$id] = $builder;
    }

    /**
     * @param PHPUnit_Framework_MockObject_Matcher_Invocation $matcher
     *
     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
     */
    public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher)
    {
        return new PHPUnit_Framework_MockObject_Builder_InvocationMocker(
            $this,
            $matcher,
            $this->configurableMethods
        );
    }

    /**
     * @param PHPUnit_Framework_MockObject_Invocation $invocation
     *
     * @return mixed
     *
     * @throws Exception
     */
    public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        $exception      = null;
        $hasReturnValue = false;
        $returnValue    = null;

        foreach ($this->matchers as $match) {
            try {
                if ($match->matches($invocation)) {
                    $value = $match->invoked($invocation);

                    if (!$hasReturnValue) {
                        $returnValue    = $value;
                        $hasReturnValue = true;
                    }
                }
            } catch (Exception $e) {
                $exception = $e;
            }
        }

        if ($exception !== null) {
            throw $exception;
        }

        if ($hasReturnValue) {
            return $returnValue;
        } elseif (strtolower($invocation->methodName) == '__tostring') {
            return '';
        }

        return $invocation->generateReturnValue();
    }

    /**
     * @param PHPUnit_Framework_MockObject_Invocation $invocation
     *
     * @return bool
     */
    public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        foreach ($this->matchers as $matcher) {
            if (!$matcher->matches($invocation)) {
                return false;
            }
        }

        return true;
    }

    /**
     * @return bool
     */
    public function verify()
    {
        foreach ($this->matchers as $matcher) {
            $matcher->verify();
        }
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Interface for classes which can be invoked.
 *
 * The invocation will be taken from a mock object and passed to an object
 * of this class.
 *
 * @since Interface available since Release 1.0.0
 */
interface PHPUnit_Framework_MockObject_Invokable extends PHPUnit_Framework_MockObject_Verifiable
{
    /**
     * Invokes the invocation object $invocation so that it can be checked for
     * expectations or matched against stubs.
     *
     * @param PHPUnit_Framework_MockObject_Invocation $invocation The invocation object passed from mock object
     *
     * @return object
     */
    public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation);

    /**
     * Checks if the invocation matches.
     *
     * @param PHPUnit_Framework_MockObject_Invocation $invocation The invocation object passed from mock object
     *
     * @return bool
     */
    public function matches(PHPUnit_Framework_MockObject_Invocation $invocation);
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Invocation matcher which checks if a method has been invoked zero or more
 * times. This matcher will always match.
 *
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount extends PHPUnit_Framework_MockObject_Matcher_InvokedRecorder
{
    /**
     * @return string
     */
    public function toString()
    {
        return 'invoked zero or more times';
    }

    /**
     */
    public function verify()
    {
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Invocation matcher which allows any parameters to a method.
 *
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_Matcher_AnyParameters extends PHPUnit_Framework_MockObject_Matcher_StatelessInvocation
{
    /**
     * @return string
     */
    public function toString()
    {
        return 'with any parameters';
    }

    /**
     * @param PHPUnit_Framework_MockObject_Invocation $invocation
     *
     * @return bool
     */
    public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        return true;
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Invocation matcher which looks for sets of specific parameters in the invocations.
 *
 * Checks the parameters of the incoming invocations, the parameter list is
 * checked against the defined constraints in $parameters. If the constraint
 * is met it will return true in matches().
 *
 * It takes a list of match groups and and increases a call index after each invocation.
 * So the first invocation uses the first group of constraints, the second the next and so on.
 */
class PHPUnit_Framework_MockObject_Matcher_ConsecutiveParameters extends PHPUnit_Framework_MockObject_Matcher_StatelessInvocation
{
    /**
     * @var array
     */
    private $parameterGroups = [];

    /**
     * @var array
     */
    private $invocations = [];

    /**
     * @param array $parameterGroups
     */
    public function __construct(array $parameterGroups)
    {
        foreach ($parameterGroups as $index => $parameters) {
            foreach ($parameters as $parameter) {
                if (!$parameter instanceof PHPUnit_Framework_Constraint) {
                    $parameter = new PHPUnit_Framework_Constraint_IsEqual($parameter);
                }

                $this->parameterGroups[$index][] = $parameter;
            }
        }
    }

    /**
     * @return string
     */
    public function toString()
    {
        $text = 'with consecutive parameters';

        return $text;
    }

    /**
     * @param PHPUnit_Framework_MockObject_Invocation $invocation
     *
     * @return bool
     */
    public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        $this->invocations[] = $invocation;
        $callIndex           = count($this->invocations) - 1;

        $this->verifyInvocation($invocation, $callIndex);

        return false;
    }

    public function verify()
    {
        foreach ($this->invocations as $callIndex => $invocation) {
            $this->verifyInvocation($invocation, $callIndex);
        }
    }

    /**
     * Verify a single invocation
     *
     * @param PHPUnit_Framework_MockObject_Invocation $invocation
     * @param int                                     $callIndex
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    private function verifyInvocation(PHPUnit_Framework_MockObject_Invocation $invocation, $callIndex)
    {
        if (isset($this->parameterGroups[$callIndex])) {
            $parameters = $this->parameterGroups[$callIndex];
        } else {
            // no parameter assertion for this call index
            return;
        }

        if ($invocation === null) {
            throw new PHPUnit_Framework_ExpectationFailedException(
                'Mocked method does not exist.'
            );
        }

        if (count($invocation->parameters) < count($parameters)) {
            throw new PHPUnit_Framework_ExpectationFailedException(
                sprintf(
                    'Parameter count for invocation %s is too low.',
                    $invocation->toString()
                )
            );
        }

        foreach ($parameters as $i => $parameter) {
            $parameter->evaluate(
                $invocation->parameters[$i],
                sprintf(
                    'Parameter %s for invocation #%d %s does not match expected ' .
                    'value.',
                    $i,
                    $callIndex,
                    $invocation->toString()
                )
            );
        }
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Interface for classes which matches an invocation based on its
 * method name, argument, order or call count.
 *
 * @since Interface available since Release 1.0.0
 */
interface PHPUnit_Framework_MockObject_Matcher_Invocation extends PHPUnit_Framework_SelfDescribing, PHPUnit_Framework_MockObject_Verifiable
{
    /**
     * Registers the invocation $invocation in the object as being invoked.
     * This will only occur after matches() returns true which means the
     * current invocation is the correct one.
     *
     * The matcher can store information from the invocation which can later
     * be checked in verify(), or it can check the values directly and throw
     * and exception if an expectation is not met.
     *
     * If the matcher is a stub it will also have a return value.
     *
     * @param PHPUnit_Framework_MockObject_Invocation $invocation Object containing information on a mocked or stubbed method which was invoked
     *
     * @return mixed
     */
    public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation);

    /**
     * Checks if the invocation $invocation matches the current rules. If it does
     * the matcher will get the invoked() method called which should check if an
     * expectation is met.
     *
     * @param PHPUnit_Framework_MockObject_Invocation $invocation Object containing information on a mocked or stubbed method which was invoked
     *
     * @return bool
     */
    public function matches(PHPUnit_Framework_MockObject_Invocation $invocation);
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Invocation matcher which checks if a method was invoked at a certain index.
 *
 * If the expected index number does not match the current invocation index it
 * will not match which means it skips all method and parameter matching. Only
 * once the index is reached will the method and parameter start matching and
 * verifying.
 *
 * If the index is never reached it will throw an exception in index.
 *
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex implements PHPUnit_Framework_MockObject_Matcher_Invocation
{
    /**
     * @var int
     */
    protected $sequenceIndex;

    /**
     * @var int
     */
    protected $currentIndex = -1;

    /**
     * @param int $sequenceIndex
     */
    public function __construct($sequenceIndex)
    {
        $this->sequenceIndex = $sequenceIndex;
    }

    /**
     * @return string
     */
    public function toString()
    {
        return 'invoked at sequence index ' . $this->sequenceIndex;
    }

    /**
     * @param PHPUnit_Framework_MockObject_Invocation $invocation
     *
     * @return bool
     */
    public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        $this->currentIndex++;

        return $this->currentIndex == $this->sequenceIndex;
    }

    /**
     * @param PHPUnit_Framework_MockObject_Invocation $invocation
     */
    public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
    }

    /**
     * Verifies that the current expectation is valid. If everything is OK the
     * code should just return, if not it must throw an exception.
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function verify()
    {
        if ($this->currentIndex < $this->sequenceIndex) {
            throw new PHPUnit_Framework_ExpectationFailedException(
                sprintf(
                    'The expected invocation at index %s was never reached.',
                    $this->sequenceIndex
                )
            );
        }
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Invocation matcher which checks if a method has been invoked at least
 * N times.
 *
 * @since Class available since Release 2.2.0
 */
class PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastCount extends PHPUnit_Framework_MockObject_Matcher_InvokedRecorder
{
    /**
     * @var int
     */
    private $requiredInvocations;

    /**
     * @param int $requiredInvocations
     */
    public function __construct($requiredInvocations)
    {
        $this->requiredInvocations = $requiredInvocations;
    }

    /**
     * @return string
     */
    public function toString()
    {
        return 'invoked at least ' . $this->requiredInvocations . ' times';
    }

    /**
     * Verifies that the current expectation is valid. If everything is OK the
     * code should just return, if not it must throw an exception.
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function verify()
    {
        $count = $this->getInvocationCount();

        if ($count < $this->requiredInvocations) {
            throw new PHPUnit_Framework_ExpectationFailedException(
                'Expected invocation at least ' . $this->requiredInvocations .
                ' times but it occurred ' . $count . ' time(s).'
            );
        }
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Invocation matcher which checks if a method has been invoked at least one
 * time.
 *
 * If the number of invocations is 0 it will throw an exception in verify.
 *
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce extends PHPUnit_Framework_MockObject_Matcher_InvokedRecorder
{
    /**
     * @return string
     */
    public function toString()
    {
        return 'invoked at least once';
    }

    /**
     * Verifies that the current expectation is valid. If everything is OK the
     * code should just return, if not it must throw an exception.
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function verify()
    {
        $count = $this->getInvocationCount();

        if ($count < 1) {
            throw new PHPUnit_Framework_ExpectationFailedException(
                'Expected invocation at least once but it never occurred.'
            );
        }
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Invocation matcher which checks if a method has been invoked at least
 * N times.
 *
 * @since Class available since Release 2.2.0
 */
class PHPUnit_Framework_MockObject_Matcher_InvokedAtMostCount extends PHPUnit_Framework_MockObject_Matcher_InvokedRecorder
{
    /**
     * @var int
     */
    private $allowedInvocations;

    /**
     * @param int $allowedInvocations
     */
    public function __construct($allowedInvocations)
    {
        $this->allowedInvocations = $allowedInvocations;
    }

    /**
     * @return string
     */
    public function toString()
    {
        return 'invoked at most ' . $this->allowedInvocations . ' times';
    }

    /**
     * Verifies that the current expectation is valid. If everything is OK the
     * code should just return, if not it must throw an exception.
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function verify()
    {
        $count = $this->getInvocationCount();

        if ($count > $this->allowedInvocations) {
            throw new PHPUnit_Framework_ExpectationFailedException(
                'Expected invocation at most ' . $this->allowedInvocations .
                ' times but it occurred ' . $count . ' time(s).'
            );
        }
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Invocation matcher which checks if a method has been invoked a certain amount
 * of times.
 * If the number of invocations exceeds the value it will immediately throw an
 * exception,
 * If the number is less it will later be checked in verify() and also throw an
 * exception.
 *
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_Matcher_InvokedCount extends PHPUnit_Framework_MockObject_Matcher_InvokedRecorder
{
    /**
     * @var int
     */
    protected $expectedCount;

    /**
     * @param int $expectedCount
     */
    public function __construct($expectedCount)
    {
        $this->expectedCount = $expectedCount;
    }

    /**
     * @return bool
     */
    public function isNever()
    {
        return $this->expectedCount == 0;
    }

    /**
     * @return string
     */
    public function toString()
    {
        return 'invoked ' . $this->expectedCount . ' time(s)';
    }

    /**
     * @param PHPUnit_Framework_MockObject_Invocation $invocation
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        parent::invoked($invocation);

        $count = $this->getInvocationCount();

        if ($count > $this->expectedCount) {
            $message = $invocation->toString() . ' ';

            switch ($this->expectedCount) {
                case 0: {
                    $message .= 'was not expected to be called.';
                }
                break;

                case 1: {
                    $message .= 'was not expected to be called more than once.';
                }
                break;

                default: {
                    $message .= sprintf(
                        'was not expected to be called more than %d times.',
                        $this->expectedCount
                    );
                    }
            }

            throw new PHPUnit_Framework_ExpectationFailedException($message);
        }
    }

    /**
     * Verifies that the current expectation is valid. If everything is OK the
     * code should just return, if not it must throw an exception.
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function verify()
    {
        $count = $this->getInvocationCount();

        if ($count !== $this->expectedCount) {
            throw new PHPUnit_Framework_ExpectationFailedException(
                sprintf(
                    'Method was expected to be called %d times, ' .
                    'actually called %d times.',
                    $this->expectedCount,
                    $count
                )
            );
        }
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Records invocations and provides convenience methods for checking them later
 * on.
 * This abstract class can be implemented by matchers which needs to check the
 * number of times an invocation has occurred.
 *
 * @since Class available since Release 1.0.0
 * @abstract
 */
abstract class PHPUnit_Framework_MockObject_Matcher_InvokedRecorder implements PHPUnit_Framework_MockObject_Matcher_Invocation
{
    /**
     * @var PHPUnit_Framework_MockObject_Invocation[]
     */
    protected $invocations = [];

    /**
     * @return int
     */
    public function getInvocationCount()
    {
        return count($this->invocations);
    }

    /**
     * @return PHPUnit_Framework_MockObject_Invocation[]
     */
    public function getInvocations()
    {
        return $this->invocations;
    }

    /**
     * @return bool
     */
    public function hasBeenInvoked()
    {
        return count($this->invocations) > 0;
    }

    /**
     * @param PHPUnit_Framework_MockObject_Invocation $invocation
     */
    public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        $this->invocations[] = $invocation;
    }

    /**
     * @param PHPUnit_Framework_MockObject_Invocation $invocation
     *
     * @return bool
     */
    public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        return true;
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Invocation matcher which looks for a specific method name in the invocations.
 *
 * Checks the method name all incoming invocations, the name is checked against
 * the defined constraint $constraint. If the constraint is met it will return
 * true in matches().
 *
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_Matcher_MethodName extends PHPUnit_Framework_MockObject_Matcher_StatelessInvocation
{
    /**
     * @var PHPUnit_Framework_Constraint
     */
    protected $constraint;

    /**
     * @param  PHPUnit_Framework_Constraint|string
     *
     * @throws PHPUnit_Framework_Constraint
     */
    public function __construct($constraint)
    {
        if (!$constraint instanceof PHPUnit_Framework_Constraint) {
            if (!is_string($constraint)) {
                throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
            }

            $constraint = new PHPUnit_Framework_Constraint_IsEqual(
                $constraint,
                0,
                10,
                false,
                true
            );
        }

        $this->constraint = $constraint;
    }

    /**
     * @return string
     */
    public function toString()
    {
        return 'method name ' . $this->constraint->toString();
    }

    /**
     * @param PHPUnit_Framework_MockObject_Invocation $invocation
     *
     * @return bool
     */
    public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        return $this->constraint->evaluate($invocation->methodName, '', true);
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Invocation matcher which looks for specific parameters in the invocations.
 *
 * Checks the parameters of all incoming invocations, the parameter list is
 * checked against the defined constraints in $parameters. If the constraint
 * is met it will return true in matches().
 *
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_Matcher_Parameters extends PHPUnit_Framework_MockObject_Matcher_StatelessInvocation
{
    /**
     * @var PHPUnit_Framework_Constraint[]
     */
    protected $parameters = [];

    /**
     * @var PHPUnit_Framework_MockObject_Invocation
     */
    protected $invocation;

    /**
     * @var PHPUnit_Framework_ExpectationFailedException
     */
    private $parameterVerificationResult;

    /**
     * @param array $parameters
     */
    public function __construct(array $parameters)
    {
        foreach ($parameters as $parameter) {
            if (!($parameter instanceof PHPUnit_Framework_Constraint)) {
                $parameter = new PHPUnit_Framework_Constraint_IsEqual(
                    $parameter
                );
            }

            $this->parameters[] = $parameter;
        }
    }

    /**
     * @return string
     */
    public function toString()
    {
        $text = 'with parameter';

        foreach ($this->parameters as $index => $parameter) {
            if ($index > 0) {
                $text .= ' and';
            }

            $text .= ' ' . $index . ' ' . $parameter->toString();
        }

        return $text;
    }

    /**
     * @param PHPUnit_Framework_MockObject_Invocation $invocation
     *
     * @return bool
     */
    public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        $this->invocation                  = $invocation;
        $this->parameterVerificationResult = null;

        try {
            $this->parameterVerificationResult = $this->verify();

            return $this->parameterVerificationResult;
        } catch (PHPUnit_Framework_ExpectationFailedException $e) {
            $this->parameterVerificationResult = $e;

            throw $this->parameterVerificationResult;
        }
    }

    /**
     * Checks if the invocation $invocation matches the current rules. If it
     * does the matcher will get the invoked() method called which should check
     * if an expectation is met.
     *
     * @return bool
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function verify()
    {
        if (isset($this->parameterVerificationResult)) {
            return $this->guardAgainstDuplicateEvaluationOfParameterConstraints();
        }

        if ($this->invocation === null) {
            throw new PHPUnit_Framework_ExpectationFailedException(
                'Mocked method does not exist.'
            );
        }

        if (count($this->invocation->parameters) < count($this->parameters)) {
            $message = 'Parameter count for invocation %s is too low.';

            // The user called `->with($this->anything())`, but may have meant
            // `->withAnyParameters()`.
            //
            // @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/199
            if (count($this->parameters) === 1 &&
                get_class($this->parameters[0]) === 'PHPUnit_Framework_Constraint_IsAnything') {
                $message .= "\nTo allow 0 or more parameters with any value, omit ->with() or use ->withAnyParameters() instead.";
            }

            throw new PHPUnit_Framework_ExpectationFailedException(
                sprintf($message, $this->invocation->toString())
            );
        }

        foreach ($this->parameters as $i => $parameter) {
            $parameter->evaluate(
                $this->invocation->parameters[$i],
                sprintf(
                    'Parameter %s for invocation %s does not match expected ' .
                    'value.',
                    $i,
                    $this->invocation->toString()
                )
            );
        }

        return true;
    }

    /**
     * @return bool
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    private function guardAgainstDuplicateEvaluationOfParameterConstraints()
    {
        if ($this->parameterVerificationResult instanceof Exception) {
            throw $this->parameterVerificationResult;
        }

        return (bool) $this->parameterVerificationResult;
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Invocation matcher which does not care about previous state from earlier
 * invocations.
 *
 * This abstract class can be implemented by matchers which does not care about
 * state but only the current run-time value of the invocation itself.
 *
 * @since Class available since Release 1.0.0
 * @abstract
 */
abstract class PHPUnit_Framework_MockObject_Matcher_StatelessInvocation implements PHPUnit_Framework_MockObject_Matcher_Invocation
{
    /**
     * Registers the invocation $invocation in the object as being invoked.
     * This will only occur after matches() returns true which means the
     * current invocation is the correct one.
     *
     * The matcher can store information from the invocation which can later
     * be checked in verify(), or it can check the values directly and throw
     * and exception if an expectation is not met.
     *
     * If the matcher is a stub it will also have a return value.
     *
     * @param PHPUnit_Framework_MockObject_Invocation $invocation Object containing information on a mocked or stubbed method which was invoked
     *
     * @return mixed
     */
    public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
    }

    /**
     * Checks if the invocation $invocation matches the current rules. If it does
     * the matcher will get the invoked() method called which should check if an
     * expectation is met.
     *
     * @param PHPUnit_Framework_MockObject_Invocation $invocation Object containing information on a mocked or stubbed method which was invoked
     *
     * @return bool
     */
    public function verify()
    {
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Main matcher which defines a full expectation using method, parameter and
 * invocation matchers.
 * This matcher encapsulates all the other matchers and allows the builder to
 * set the specific matchers when the appropriate methods are called (once(),
 * where() etc.).
 *
 * All properties are public so that they can easily be accessed by the builder.
 *
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_Matcher implements PHPUnit_Framework_MockObject_Matcher_Invocation
{
    /**
     * @var PHPUnit_Framework_MockObject_Matcher_Invocation
     */
    public $invocationMatcher;

    /**
     * @var mixed
     */
    public $afterMatchBuilderId = null;

    /**
     * @var bool
     */
    public $afterMatchBuilderIsInvoked = false;

    /**
     * @var PHPUnit_Framework_MockObject_Matcher_MethodName
     */
    public $methodNameMatcher = null;

    /**
     * @var PHPUnit_Framework_MockObject_Matcher_Parameters
     */
    public $parametersMatcher = null;

    /**
     * @var PHPUnit_Framework_MockObject_Stub
     */
    public $stub = null;

    /**
     * @param PHPUnit_Framework_MockObject_Matcher_Invocation $invocationMatcher
     */
    public function __construct(PHPUnit_Framework_MockObject_Matcher_Invocation $invocationMatcher)
    {
        $this->invocationMatcher = $invocationMatcher;
    }

    /**
     * @return string
     */
    public function toString()
    {
        $list = [];

        if ($this->invocationMatcher !== null) {
            $list[] = $this->invocationMatcher->toString();
        }

        if ($this->methodNameMatcher !== null) {
            $list[] = 'where ' . $this->methodNameMatcher->toString();
        }

        if ($this->parametersMatcher !== null) {
            $list[] = 'and ' . $this->parametersMatcher->toString();
        }

        if ($this->afterMatchBuilderId !== null) {
            $list[] = 'after ' . $this->afterMatchBuilderId;
        }

        if ($this->stub !== null) {
            $list[] = 'will ' . $this->stub->toString();
        }

        return implode(' ', $list);
    }

    /**
     * @param PHPUnit_Framework_MockObject_Invocation $invocation
     *
     * @return mixed
     */
    public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        if ($this->invocationMatcher === null) {
            throw new PHPUnit_Framework_MockObject_RuntimeException(
                'No invocation matcher is set'
            );
        }

        if ($this->methodNameMatcher === null) {
            throw new PHPUnit_Framework_MockObject_RuntimeException('No method matcher is set');
        }

        if ($this->afterMatchBuilderId !== null) {
            $builder = $invocation->object
                                  ->__phpunit_getInvocationMocker()
                                  ->lookupId($this->afterMatchBuilderId);

            if (!$builder) {
                throw new PHPUnit_Framework_MockObject_RuntimeException(
                    sprintf(
                        'No builder found for match builder identification <%s>',
                        $this->afterMatchBuilderId
                    )
                );
            }

            $matcher = $builder->getMatcher();

            if ($matcher && $matcher->invocationMatcher->hasBeenInvoked()) {
                $this->afterMatchBuilderIsInvoked = true;
            }
        }

        $this->invocationMatcher->invoked($invocation);

        try {
            if ($this->parametersMatcher !== null &&
                !$this->parametersMatcher->matches($invocation)) {
                $this->parametersMatcher->verify();
            }
        } catch (PHPUnit_Framework_ExpectationFailedException $e) {
            throw new PHPUnit_Framework_ExpectationFailedException(
                sprintf(
                    "Expectation failed for %s when %s\n%s",
                    $this->methodNameMatcher->toString(),
                    $this->invocationMatcher->toString(),
                    $e->getMessage()
                ),
                $e->getComparisonFailure()
            );
        }

        if ($this->stub) {
            return $this->stub->invoke($invocation);
        }

        return $invocation->generateReturnValue();
    }

    /**
     * @param PHPUnit_Framework_MockObject_Invocation $invocation
     *
     * @return bool
     */
    public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        if ($this->afterMatchBuilderId !== null) {
            $builder = $invocation->object
                                  ->__phpunit_getInvocationMocker()
                                  ->lookupId($this->afterMatchBuilderId);

            if (!$builder) {
                throw new PHPUnit_Framework_MockObject_RuntimeException(
                    sprintf(
                        'No builder found for match builder identification <%s>',
                        $this->afterMatchBuilderId
                    )
                );
            }

            $matcher = $builder->getMatcher();

            if (!$matcher) {
                return false;
            }

            if (!$matcher->invocationMatcher->hasBeenInvoked()) {
                return false;
            }
        }

        if ($this->invocationMatcher === null) {
            throw new PHPUnit_Framework_MockObject_RuntimeException(
                'No invocation matcher is set'
            );
        }

        if ($this->methodNameMatcher === null) {
            throw new PHPUnit_Framework_MockObject_RuntimeException('No method matcher is set');
        }

        if (!$this->invocationMatcher->matches($invocation)) {
            return false;
        }

        try {
            if (!$this->methodNameMatcher->matches($invocation)) {
                return false;
            }
        } catch (PHPUnit_Framework_ExpectationFailedException $e) {
            throw new PHPUnit_Framework_ExpectationFailedException(
                sprintf(
                    "Expectation failed for %s when %s\n%s",
                    $this->methodNameMatcher->toString(),
                    $this->invocationMatcher->toString(),
                    $e->getMessage()
                ),
                $e->getComparisonFailure()
            );
        }

        return true;
    }

    /**
     * @throws PHPUnit_Framework_MockObject_RuntimeException
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function verify()
    {
        if ($this->invocationMatcher === null) {
            throw new PHPUnit_Framework_MockObject_RuntimeException(
                'No invocation matcher is set'
            );
        }

        if ($this->methodNameMatcher === null) {
            throw new PHPUnit_Framework_MockObject_RuntimeException('No method matcher is set');
        }

        try {
            $this->invocationMatcher->verify();

            if ($this->parametersMatcher === null) {
                $this->parametersMatcher = new PHPUnit_Framework_MockObject_Matcher_AnyParameters;
            }

            $invocationIsAny   = $this->invocationMatcher instanceof PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount;
            $invocationIsNever = $this->invocationMatcher instanceof PHPUnit_Framework_MockObject_Matcher_InvokedCount && $this->invocationMatcher->isNever();

            if (!$invocationIsAny && !$invocationIsNever) {
                $this->parametersMatcher->verify();
            }
        } catch (PHPUnit_Framework_ExpectationFailedException $e) {
            throw new PHPUnit_Framework_ExpectationFailedException(
                sprintf(
                    "Expectation failed for %s when %s.\n%s",
                    $this->methodNameMatcher->toString(),
                    $this->invocationMatcher->toString(),
                    PHPUnit_Framework_TestFailure::exceptionToString($e)
                )
            );
        }
    }

    /**
     * @since Method available since Release 1.2.4
     */
    public function hasMatchers()
    {
        if ($this->invocationMatcher !== null &&
            !$this->invocationMatcher instanceof PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount) {
            return true;
        }

        return false;
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Implementation of the Builder pattern for Mock objects.
 *
 * @since File available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_MockBuilder
{
    /**
     * @var PHPUnit_Framework_TestCase
     */
    private $testCase;

    /**
     * @var string
     */
    private $type;

    /**
     * @var array
     */
    private $methods = [];

    /**
     * @var array
     */
    private $methodsExcept = [];

    /**
     * @var string
     */
    private $mockClassName = '';

    /**
     * @var array
     */
    private $constructorArgs = [];

    /**
     * @var bool
     */
    private $originalConstructor = true;

    /**
     * @var bool
     */
    private $originalClone = true;

    /**
     * @var bool
     */
    private $autoload = true;

    /**
     * @var bool
     */
    private $cloneArguments = false;

    /**
     * @var bool
     */
    private $callOriginalMethods = false;

    /**
     * @var object
     */
    private $proxyTarget = null;

    /**
     * @var bool
     */
    private $allowMockingUnknownTypes = true;

    /**
     * @var PHPUnit_Framework_MockObject_Generator
     */
    private $generator;

    /**
     * @param PHPUnit_Framework_TestCase $testCase
     * @param array|string               $type
     */
    public function __construct(PHPUnit_Framework_TestCase $testCase, $type)
    {
        $this->testCase  = $testCase;
        $this->type      = $type;
        $this->generator = new PHPUnit_Framework_MockObject_Generator;
    }

    /**
     * Creates a mock object using a fluent interface.
     *
     * @return PHPUnit_Framework_MockObject_MockObject
     */
    public function getMock()
    {
        $object = $this->generator->getMock(
            $this->type,
            $this->methods,
            $this->constructorArgs,
            $this->mockClassName,
            $this->originalConstructor,
            $this->originalClone,
            $this->autoload,
            $this->cloneArguments,
            $this->callOriginalMethods,
            $this->proxyTarget,
            $this->allowMockingUnknownTypes
        );

        $this->testCase->registerMockObject($object);

        return $object;
    }

    /**
     * Creates a mock object for an abstract class using a fluent interface.
     *
     * @return PHPUnit_Framework_MockObject_MockObject
     */
    public function getMockForAbstractClass()
    {
        $object = $this->generator->getMockForAbstractClass(
            $this->type,
            $this->constructorArgs,
            $this->mockClassName,
            $this->originalConstructor,
            $this->originalClone,
            $this->autoload,
            $this->methods,
            $this->cloneArguments
        );

        $this->testCase->registerMockObject($object);

        return $object;
    }

    /**
     * Creates a mock object for a trait using a fluent interface.
     *
     * @return PHPUnit_Framework_MockObject_MockObject
     */
    public function getMockForTrait()
    {
        $object = $this->generator->getMockForTrait(
            $this->type,
            $this->constructorArgs,
            $this->mockClassName,
            $this->originalConstructor,
            $this->originalClone,
            $this->autoload,
            $this->methods,
            $this->cloneArguments
        );

        $this->testCase->registerMockObject($object);

        return $object;
    }

    /**
     * Specifies the subset of methods to mock. Default is to mock all of them.
     *
     * @param array|null $methods
     *
     * @return PHPUnit_Framework_MockObject_MockBuilder
     */
    public function setMethods(array $methods = null)
    {
        $this->methods = $methods;

        return $this;
    }

    /**
     * Specifies the subset of methods to not mock. Default is to mock all of them.
     *
     * @param array $methods
     *
     * @return PHPUnit_Framework_MockObject_MockBuilder
     */
    public function setMethodsExcept(array $methods = [])
    {
        $this->methodsExcept = $methods;

        $this->setMethods(
            array_diff(
                $this->generator->getClassMethods($this->type),
                $this->methodsExcept
            )
        );

        return $this;
    }

    /**
     * Specifies the arguments for the constructor.
     *
     * @param array $args
     *
     * @return PHPUnit_Framework_MockObject_MockBuilder
     */
    public function setConstructorArgs(array $args)
    {
        $this->constructorArgs = $args;

        return $this;
    }

    /**
     * Specifies the name for the mock class.
     *
     * @param string $name
     *
     * @return PHPUnit_Framework_MockObject_MockBuilder
     */
    public function setMockClassName($name)
    {
        $this->mockClassName = $name;

        return $this;
    }

    /**
     * Disables the invocation of the original constructor.
     *
     * @return PHPUnit_Framework_MockObject_MockBuilder
     */
    public function disableOriginalConstructor()
    {
        $this->originalConstructor = false;

        return $this;
    }

    /**
     * Enables the invocation of the original constructor.
     *
     * @return PHPUnit_Framework_MockObject_MockBuilder
     *
     * @since  Method available since Release 1.2.0
     */
    public function enableOriginalConstructor()
    {
        $this->originalConstructor = true;

        return $this;
    }

    /**
     * Disables the invocation of the original clone constructor.
     *
     * @return PHPUnit_Framework_MockObject_MockBuilder
     */
    public function disableOriginalClone()
    {
        $this->originalClone = false;

        return $this;
    }

    /**
     * Enables the invocation of the original clone constructor.
     *
     * @return PHPUnit_Framework_MockObject_MockBuilder
     *
     * @since  Method available since Release 1.2.0
     */
    public function enableOriginalClone()
    {
        $this->originalClone = true;

        return $this;
    }

    /**
     * Disables the use of class autoloading while creating the mock object.
     *
     * @return PHPUnit_Framework_MockObject_MockBuilder
     */
    public function disableAutoload()
    {
        $this->autoload = false;

        return $this;
    }

    /**
     * Enables the use of class autoloading while creating the mock object.
     *
     * @return PHPUnit_Framework_MockObject_MockBuilder
     *
     * @since  Method available since Release 1.2.0
     */
    public function enableAutoload()
    {
        $this->autoload = true;

        return $this;
    }

    /**
     * Disables the cloning of arguments passed to mocked methods.
     *
     * @return PHPUnit_Framework_MockObject_MockBuilder
     *
     * @since  Method available since Release 1.2.0
     */
    public function disableArgumentCloning()
    {
        $this->cloneArguments = false;

        return $this;
    }

    /**
     * Enables the cloning of arguments passed to mocked methods.
     *
     * @return PHPUnit_Framework_MockObject_MockBuilder
     *
     * @since  Method available since Release 1.2.0
     */
    public function enableArgumentCloning()
    {
        $this->cloneArguments = true;

        return $this;
    }

    /**
     * Enables the invocation of the original methods.
     *
     * @return PHPUnit_Framework_MockObject_MockBuilder
     *
     * @since  Method available since Release 2.0.0
     */
    public function enableProxyingToOriginalMethods()
    {
        $this->callOriginalMethods = true;

        return $this;
    }

    /**
     * Disables the invocation of the original methods.
     *
     * @return PHPUnit_Framework_MockObject_MockBuilder
     *
     * @since  Method available since Release 2.0.0
     */
    public function disableProxyingToOriginalMethods()
    {
        $this->callOriginalMethods = false;
        $this->proxyTarget         = null;

        return $this;
    }

    /**
     * Sets the proxy target.
     *
     * @param object $object
     *
     * @return PHPUnit_Framework_MockObject_MockBuilder
     *
     * @since  Method available since Release 2.0.0
     */
    public function setProxyTarget($object)
    {
        $this->proxyTarget = $object;

        return $this;
    }

    /**
     * @return PHPUnit_Framework_MockObject_MockBuilder
     *
     * @since  Method available since Release 3.2.0
     */
    public function allowMockingUnknownTypes()
    {
        $this->allowMockingUnknownTypes = true;

        return $this;
    }

    /**
     * @return PHPUnit_Framework_MockObject_MockBuilder
     *
     * @since  Method available since Release 3.2.0
     */
    public function disallowMockingUnknownTypes()
    {
        $this->allowMockingUnknownTypes = false;

        return $this;
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Interface for all mock objects which are generated by
 * PHPUnit_Framework_MockObject_MockBuilder.
 *
 * @method PHPUnit_Framework_MockObject_Builder_InvocationMocker method($constraint)
 *
 * @since Interface available since Release 1.0.0
 */
interface PHPUnit_Framework_MockObject_MockObject /*extends PHPUnit_Framework_MockObject_Verifiable*/
{
    /**
     * Registers a new expectation in the mock object and returns the match
     * object which can be infused with further details.
     *
     * @param PHPUnit_Framework_MockObject_Matcher_Invocation $matcher
     *
     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
     */
    public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher);

    /**
     * @return PHPUnit_Framework_MockObject_InvocationMocker
     *
     * @since  Method available since Release 2.0.0
     */
    public function __phpunit_setOriginalObject($originalObject);

    /**
     * @return PHPUnit_Framework_MockObject_InvocationMocker
     */
    public function __phpunit_getInvocationMocker();

    /**
     * Verifies that the current expectation is valid. If everything is OK the
     * code should just return, if not it must throw an exception.
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function __phpunit_verify();

    /**
     * @return bool
     */
    public function __phpunit_hasMatchers();
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use SebastianBergmann\Exporter\Exporter;

/**
 * Stubs a method by returning a user-defined stack of values.
 *
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls implements PHPUnit_Framework_MockObject_Stub
{
    protected $stack;
    protected $value;

    public function __construct($stack)
    {
        $this->stack = $stack;
    }

    public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        $this->value = array_shift($this->stack);

        if ($this->value instanceof PHPUnit_Framework_MockObject_Stub) {
            $this->value = $this->value->invoke($invocation);
        }

        return $this->value;
    }

    public function toString()
    {
        $exporter = new Exporter;

        return sprintf(
            'return user-specified value %s',
            $exporter->export($this->value)
        );
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use SebastianBergmann\Exporter\Exporter;

/**
 * Stubs a method by raising a user-defined exception.
 *
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_Stub_Exception implements PHPUnit_Framework_MockObject_Stub
{
    protected $exception;

    public function __construct($exception)
    {
        // TODO Replace check with type declaration when support for PHP 5 is dropped
        if (!$exception instanceof Throwable && !$exception instanceof Exception) {
            throw new PHPUnit_Framework_MockObject_RuntimeException(
                'Exception must be an instance of Throwable (PHP 7) or Exception (PHP 5)'
            );
        }

        $this->exception = $exception;
    }

    public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        throw $this->exception;
    }

    public function toString()
    {
        $exporter = new Exporter;

        return sprintf(
            'raise user-specified exception %s',
            $exporter->export($this->exception)
        );
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Stubs a method by returning a user-defined value.
 *
 * @since Interface available since Release 1.0.0
 */
interface PHPUnit_Framework_MockObject_Stub_MatcherCollection
{
    /**
     * Adds a new matcher to the collection which can be used as an expectation
     * or a stub.
     *
     * @param PHPUnit_Framework_MockObject_Matcher_Invocation $matcher Matcher for invocations to mock objects
     */
    public function addMatcher(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher);
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use SebastianBergmann\Exporter\Exporter;

/**
 * Stubs a method by returning a user-defined value.
 *
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_Stub_Return implements PHPUnit_Framework_MockObject_Stub
{
    protected $value;

    public function __construct($value)
    {
        $this->value = $value;
    }

    public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        return $this->value;
    }

    public function toString()
    {
        $exporter = new Exporter;

        return sprintf(
            'return user-specified value %s',
            $exporter->export($this->value)
        );
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Stubs a method by returning an argument that was passed to the mocked method.
 *
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_Stub_ReturnArgument extends PHPUnit_Framework_MockObject_Stub_Return
{
    protected $argumentIndex;

    public function __construct($argumentIndex)
    {
        $this->argumentIndex = $argumentIndex;
    }

    public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        if (isset($invocation->parameters[$this->argumentIndex])) {
            return $invocation->parameters[$this->argumentIndex];
        } else {
            return;
        }
    }

    public function toString()
    {
        return sprintf('return argument #%d', $this->argumentIndex);
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * @since Class available since Release 1.0.0
 */
class PHPUnit_Framework_MockObject_Stub_ReturnCallback implements PHPUnit_Framework_MockObject_Stub
{
    protected $callback;

    public function __construct($callback)
    {
        $this->callback = $callback;
    }

    public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        return call_user_func_array($this->callback, $invocation->parameters);
    }

    public function toString()
    {
        if (is_array($this->callback)) {
            if (is_object($this->callback[0])) {
                $class = get_class($this->callback[0]);
                $type  = '->';
            } else {
                $class = $this->callback[0];
                $type  = '::';
            }

            return sprintf(
                'return result of user defined callback %s%s%s() with the ' .
                'passed arguments',
                $class,
                $type,
                $this->callback[1]
            );
        } else {
            return 'return result of user defined callback ' . $this->callback .
                   ' with the passed arguments';
        }
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Stubs a method by returning a user-defined reference to a value.
 *
 * @since Class available since Release 3.0.7
 */
class PHPUnit_Framework_MockObject_Stub_ReturnReference extends PHPUnit_Framework_MockObject_Stub_Return
{
    public function __construct(&$value)
    {
        $this->value = &$value;
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Stubs a method by returning the current object.
 *
 * @since Class available since Release 1.1.0
 */
class PHPUnit_Framework_MockObject_Stub_ReturnSelf implements PHPUnit_Framework_MockObject_Stub
{
    public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        if (!$invocation instanceof PHPUnit_Framework_MockObject_Invocation_Object) {
            throw new PHPUnit_Framework_MockObject_RuntimeException(
                'The current object can only be returned when mocking an ' .
                'object, not a static class.'
            );
        }

        return $invocation->object;
    }

    public function toString()
    {
        return 'return the current object';
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Stubs a method by returning a value from a map.
 *
 * @since Class available since Release 1.1.0
 */
class PHPUnit_Framework_MockObject_Stub_ReturnValueMap implements PHPUnit_Framework_MockObject_Stub
{
    protected $valueMap;

    public function __construct(array $valueMap)
    {
        $this->valueMap = $valueMap;
    }

    public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation)
    {
        $parameterCount = count($invocation->parameters);

        foreach ($this->valueMap as $map) {
            if (!is_array($map) || $parameterCount != count($map) - 1) {
                continue;
            }

            $return = array_pop($map);
            if ($invocation->parameters === $map) {
                return $return;
            }
        }

        return;
    }

    public function toString()
    {
        return 'return value from a map';
    }
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * An object that stubs the process of a normal method for a mock object.
 *
 * The stub object will replace the code for the stubbed method and return a
 * specific value instead of the original value.
 *
 * @since Interface available since Release 1.0.0
 */
interface PHPUnit_Framework_MockObject_Stub extends PHPUnit_Framework_SelfDescribing
{
    /**
     * Fakes the processing of the invocation $invocation by returning a
     * specific value.
     *
     * @param PHPUnit_Framework_MockObject_Invocation $invocation The invocation which was mocked and matched by the current method and argument matchers
     *
     * @return mixed
     */
    public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation);
}
<?php
/*
 * This file is part of the PHPUnit_MockObject package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Interface for classes which must verify a given expectation.
 *
 * @since Interface available since Release 1.0.0
 */
interface PHPUnit_Framework_MockObject_Verifiable
{
    /**
     * Verifies that the current expectation is valid. If everything is OK the
     * code should just return, if not it must throw an exception.
     *
     * @throws PHPUnit_Framework_ExpectationFailedException
     */
    public function verify();
}
<?php
/*
 * This file is part of code-unit-reverse-lookup.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\CodeUnitReverseLookup;

/**
 * @since Class available since Release 1.0.0
 */
class Wizard
{
    /**
     * @var array
     */
    private $lookupTable = [];

    /**
     * @var array
     */
    private $processedClasses = [];

    /**
     * @var array
     */
    private $processedFunctions = [];

    /**
     * @param string $filename
     * @param int    $lineNumber
     *
     * @return string
     */
    public function lookup($filename, $lineNumber)
    {
        if (!isset($this->lookupTable[$filename][$lineNumber])) {
            $this->updateLookupTable();
        }

        if (isset($this->lookupTable[$filename][$lineNumber])) {
            return $this->lookupTable[$filename][$lineNumber];
        } else {
            return $filename . ':' . $lineNumber;
        }
    }

    private function updateLookupTable()
    {
        $this->processClassesAndTraits();
        $this->processFunctions();
    }

    private function processClassesAndTraits()
    {
        foreach (array_merge(get_declared_classes(), get_declared_traits()) as $classOrTrait) {
            if (isset($this->processedClasses[$classOrTrait])) {
                continue;
            }

            $reflector = new \ReflectionClass($classOrTrait);

            foreach ($reflector->getMethods() as $method) {
                $this->processFunctionOrMethod($method);
            }

            $this->processedClasses[$classOrTrait] = true;
        }
    }

    private function processFunctions()
    {
        foreach (get_defined_functions()['user'] as $function) {
            if (isset($this->processedFunctions[$function])) {
                continue;
            }

            $this->processFunctionOrMethod(new \ReflectionFunction($function));

            $this->processedFunctions[$function] = true;
        }
    }

    /**
     * @param \ReflectionFunctionAbstract $functionOrMethod
     */
    private function processFunctionOrMethod(\ReflectionFunctionAbstract $functionOrMethod)
    {
        if ($functionOrMethod->isInternal()) {
            return;
        }

        $name = $functionOrMethod->getName();

        if ($functionOrMethod instanceof \ReflectionMethod) {
            $name = $functionOrMethod->getDeclaringClass()->getName() . '::' . $name;
        }

        if (!isset($this->lookupTable[$functionOrMethod->getFileName()])) {
            $this->lookupTable[$functionOrMethod->getFileName()] = [];
        }

        foreach (range($functionOrMethod->getStartLine(), $functionOrMethod->getEndLine()) as $line) {
            $this->lookupTable[$functionOrMethod->getFileName()][$line] = $name;
        }
    }
}
<?php
/*
 * This file is part of the Comparator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Comparator;

/**
 * Compares arrays for equality.
 */
class ArrayComparator extends Comparator
{
    /**
     * Returns whether the comparator can compare two values.
     *
     * @param  mixed $expected The first value to compare
     * @param  mixed $actual   The second value to compare
     * @return bool
     */
    public function accepts($expected, $actual)
    {
        return is_array($expected) && is_array($actual);
    }

    /**
     * Asserts that two values are equal.
     *
     * @param mixed $expected     First value to compare
     * @param mixed $actual       Second value to compare
     * @param float $delta        Allowed numerical distance between two values to consider them equal
     * @param bool  $canonicalize Arrays are sorted before comparison when set to true
     * @param bool  $ignoreCase   Case is ignored when set to true
     * @param array $processed    List of already processed elements (used to prevent infinite recursion)
     *
     * @throws ComparisonFailure
     */
    public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array())
    {
        if ($canonicalize) {
            sort($expected);
            sort($actual);
        }

        $remaining = $actual;
        $expString = $actString = "Array (\n";
        $equal     = true;

        foreach ($expected as $key => $value) {
            unset($remaining[$key]);

            if (!array_key_exists($key, $actual)) {
                $expString .= sprintf(
                    "    %s => %s\n",
                    $this->exporter->export($key),
                    $this->exporter->shortenedExport($value)
                );

                $equal = false;

                continue;
            }

            try {
                $comparator = $this->factory->getComparatorFor($value, $actual[$key]);
                $comparator->assertEquals($value, $actual[$key], $delta, $canonicalize, $ignoreCase, $processed);

                $expString .= sprintf(
                    "    %s => %s\n",
                    $this->exporter->export($key),
                    $this->exporter->shortenedExport($value)
                );
                $actString .= sprintf(
                    "    %s => %s\n",
                    $this->exporter->export($key),
                    $this->exporter->shortenedExport($actual[$key])
                );
            } catch (ComparisonFailure $e) {
                $expString .= sprintf(
                    "    %s => %s\n",
                    $this->exporter->export($key),
                    $e->getExpectedAsString()
                    ? $this->indent($e->getExpectedAsString())
                    : $this->exporter->shortenedExport($e->getExpected())
                );

                $actString .= sprintf(
                    "    %s => %s\n",
                    $this->exporter->export($key),
                    $e->getActualAsString()
                    ? $this->indent($e->getActualAsString())
                    : $this->exporter->shortenedExport($e->getActual())
                );

                $equal = false;
            }
        }

        foreach ($remaining as $key => $value) {
            $actString .= sprintf(
                "    %s => %s\n",
                $this->exporter->export($key),
                $this->exporter->shortenedExport($value)
            );

            $equal = false;
        }

        $expString .= ')';
        $actString .= ')';

        if (!$equal) {
            throw new ComparisonFailure(
                $expected,
                $actual,
                $expString,
                $actString,
                false,
                'Failed asserting that two arrays are equal.'
            );
        }
    }

    protected function indent($lines)
    {
        return trim(str_replace("\n", "\n    ", $lines));
    }
}
<?php
/*
 * This file is part of the Comparator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Comparator;

use SebastianBergmann\Exporter\Exporter;

/**
 * Abstract base class for comparators which compare values for equality.
 */
abstract class Comparator
{
    /**
     * @var Factory
     */
    protected $factory;

    /**
     * @var Exporter
     */
    protected $exporter;

    public function __construct()
    {
        $this->exporter = new Exporter;
    }

    /**
     * @param Factory $factory
     */
    public function setFactory(Factory $factory)
    {
        $this->factory = $factory;
    }

    /**
     * Returns whether the comparator can compare two values.
     *
     * @param  mixed $expected The first value to compare
     * @param  mixed $actual   The second value to compare
     * @return bool
     */
    abstract public function accepts($expected, $actual);

    /**
     * Asserts that two values are equal.
     *
     * @param mixed $expected     First value to compare
     * @param mixed $actual       Second value to compare
     * @param float $delta        Allowed numerical distance between two values to consider them equal
     * @param bool  $canonicalize Arrays are sorted before comparison when set to true
     * @param bool  $ignoreCase   Case is ignored when set to true
     *
     * @throws ComparisonFailure
     */
    abstract public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false);
}
<?php
/*
 * This file is part of the Comparator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Comparator;

use SebastianBergmann\Diff\Differ;

/**
 * Thrown when an assertion for string equality failed.
 */
class ComparisonFailure extends \RuntimeException
{
    /**
     * Expected value of the retrieval which does not match $actual.
     * @var mixed
     */
    protected $expected;

    /**
     * Actually retrieved value which does not match $expected.
     * @var mixed
     */
    protected $actual;

    /**
     * The string representation of the expected value
     * @var string
     */
    protected $expectedAsString;

    /**
     * The string representation of the actual value
     * @var string
     */
    protected $actualAsString;

    /**
     * @var bool
     */
    protected $identical;

    /**
     * Optional message which is placed in front of the first line
     * returned by toString().
     * @var string
     */
    protected $message;

    /**
     * Initialises with the expected value and the actual value.
     *
     * @param mixed  $expected         Expected value retrieved.
     * @param mixed  $actual           Actual value retrieved.
     * @param string $expectedAsString
     * @param string $actualAsString
     * @param bool   $identical
     * @param string $message          A string which is prefixed on all returned lines
     *                                 in the difference output.
     */
    public function __construct($expected, $actual, $expectedAsString, $actualAsString, $identical = false, $message = '')
    {
        $this->expected         = $expected;
        $this->actual           = $actual;
        $this->expectedAsString = $expectedAsString;
        $this->actualAsString   = $actualAsString;
        $this->message          = $message;
    }

    /**
     * @return mixed
     */
    public function getActual()
    {
        return $this->actual;
    }

    /**
     * @return mixed
     */
    public function getExpected()
    {
        return $this->expected;
    }

    /**
     * @return string
     */
    public function getActualAsString()
    {
        return $this->actualAsString;
    }

    /**
     * @return string
     */
    public function getExpectedAsString()
    {
        return $this->expectedAsString;
    }

    /**
     * @return string
     */
    public function getDiff()
    {
        if (!$this->actualAsString && !$this->expectedAsString) {
            return '';
        }

        $differ = new Differ("\n--- Expected\n+++ Actual\n");

        return $differ->diff($this->expectedAsString, $this->actualAsString);
    }

    /**
     * @return string
     */
    public function toString()
    {
        return $this->message . $this->getDiff();
    }
}
<?php
/*
 * This file is part of the Comparator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Comparator;

/**
 * Compares DateTimeInterface instances for equality.
 */
class DateTimeComparator extends ObjectComparator
{
    /**
     * Returns whether the comparator can compare two values.
     *
     * @param  mixed $expected The first value to compare
     * @param  mixed $actual   The second value to compare
     * @return bool
     */
    public function accepts($expected, $actual)
    {
        return ($expected instanceof \DateTime || $expected instanceof \DateTimeInterface) &&
            ($actual instanceof \DateTime || $actual instanceof \DateTimeInterface);
    }

    /**
     * Asserts that two values are equal.
     *
     * @param mixed $expected     First value to compare
     * @param mixed $actual       Second value to compare
     * @param float $delta        Allowed numerical distance between two values to consider them equal
     * @param bool  $canonicalize Arrays are sorted before comparison when set to true
     * @param bool  $ignoreCase   Case is ignored when set to true
     * @param array $processed    List of already processed elements (used to prevent infinite recursion)
     *
     * @throws ComparisonFailure
     */
    public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array())
    {
        $delta = new \DateInterval(sprintf('PT%sS', abs($delta)));

        $expectedLower = clone $expected;
        $expectedUpper = clone $expected;

        if ($actual < $expectedLower->sub($delta) ||
            $actual > $expectedUpper->add($delta)) {
            throw new ComparisonFailure(
                $expected,
                $actual,
                $this->dateTimeToString($expected),
                $this->dateTimeToString($actual),
                false,
                'Failed asserting that two DateTime objects are equal.'
            );
        }
    }

    /**
     * Returns an ISO 8601 formatted string representation of a datetime or
     * 'Invalid DateTimeInterface object' if the provided DateTimeInterface was not properly
     * initialized.
     *
     * @param  \DateTimeInterface $datetime
     * @return string
     */
    private function dateTimeToString($datetime)
    {
        $string = $datetime->format('Y-m-d\TH:i:s.uO');

        return $string ? $string : 'Invalid DateTimeInterface object';
    }
}
<?php
/*
 * This file is part of the Comparator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Comparator;

use DOMDocument;
use DOMNode;

/**
 * Compares DOMNode instances for equality.
 */
class DOMNodeComparator extends ObjectComparator
{
    /**
     * Returns whether the comparator can compare two values.
     *
     * @param  mixed $expected The first value to compare
     * @param  mixed $actual   The second value to compare
     * @return bool
     */
    public function accepts($expected, $actual)
    {
        return $expected instanceof DOMNode && $actual instanceof DOMNode;
    }

    /**
     * Asserts that two values are equal.
     *
     * @param mixed $expected     First value to compare
     * @param mixed $actual       Second value to compare
     * @param float $delta        Allowed numerical distance between two values to consider them equal
     * @param bool  $canonicalize Arrays are sorted before comparison when set to true
     * @param bool  $ignoreCase   Case is ignored when set to true
     * @param array $processed    List of already processed elements (used to prevent infinite recursion)
     *
     * @throws ComparisonFailure
     */
    public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array())
    {
        $expectedAsString = $this->nodeToText($expected, true, $ignoreCase);
        $actualAsString   = $this->nodeToText($actual, true, $ignoreCase);

        if ($expectedAsString !== $actualAsString) {
            if ($expected instanceof DOMDocument) {
                $type = 'documents';
            } else {
                $type = 'nodes';
            }

            throw new ComparisonFailure(
                $expected,
                $actual,
                $expectedAsString,
                $actualAsString,
                false,
                sprintf("Failed asserting that two DOM %s are equal.\n", $type)
            );
        }
    }

    /**
     * Returns the normalized, whitespace-cleaned, and indented textual
     * representation of a DOMNode.
     *
     * @param  DOMNode $node
     * @param  bool    $canonicalize
     * @param  bool    $ignoreCase
     * @return string
     */
    private function nodeToText(DOMNode $node, $canonicalize, $ignoreCase)
    {
        if ($canonicalize) {
            $document = new DOMDocument;
            $document->loadXML($node->C14N());

            $node = $document;
        }

        if ($node instanceof DOMDocument) {
            $document = $node;
        } else {
            $document = $node->ownerDocument;
        }

        $document->formatOutput = true;
        $document->normalizeDocument();

        if ($node instanceof DOMDocument) {
            $text = $node->saveXML();
        } else {
            $text = $document->saveXML($node);
        }

        if ($ignoreCase) {
            $text = strtolower($text);
        }

        return $text;
    }
}
<?php
/*
 * This file is part of the Comparator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Comparator;

/**
 * Compares doubles for equality.
 */
class DoubleComparator extends NumericComparator
{
    /**
     * Smallest value available in PHP.
     *
     * @var float
     */
    const EPSILON = 0.0000000001;

    /**
     * Returns whether the comparator can compare two values.
     *
     * @param  mixed $expected The first value to compare
     * @param  mixed $actual   The second value to compare
     * @return bool
     */
    public function accepts($expected, $actual)
    {
        return (is_double($expected) || is_double($actual)) && is_numeric($expected) && is_numeric($actual);
    }

    /**
     * Asserts that two values are equal.
     *
     * @param mixed $expected     First value to compare
     * @param mixed $actual       Second value to compare
     * @param float $delta        Allowed numerical distance between two values to consider them equal
     * @param bool  $canonicalize Arrays are sorted before comparison when set to true
     * @param bool  $ignoreCase   Case is ignored when set to true
     *
     * @throws ComparisonFailure
     */
    public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
    {
        if ($delta == 0) {
            $delta = self::EPSILON;
        }

        parent::assertEquals($expected, $actual, $delta, $canonicalize, $ignoreCase);
    }
}
<?php
/*
 * This file is part of the Comparator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Comparator;

/**
 * Compares Exception instances for equality.
 */
class ExceptionComparator extends ObjectComparator
{
    /**
     * Returns whether the comparator can compare two values.
     *
     * @param  mixed $expected The first value to compare
     * @param  mixed $actual   The second value to compare
     * @return bool
     */
    public function accepts($expected, $actual)
    {
        return $expected instanceof \Exception && $actual instanceof \Exception;
    }

    /**
     * Converts an object to an array containing all of its private, protected
     * and public properties.
     *
     * @param  object $object
     * @return array
     */
    protected function toArray($object)
    {
        $array = parent::toArray($object);

        unset(
            $array['file'],
            $array['line'],
            $array['trace'],
            $array['string'],
            $array['xdebug_message']
        );

        return $array;
    }
}
<?php
/*
 * This file is part of the Comparator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Comparator;

/**
 * Factory for comparators which compare values for equality.
 */
class Factory
{
    /**
     * @var Comparator[]
     */
    private $comparators = array();

    /**
     * @var Factory
     */
    private static $instance;

    /**
     * Constructs a new factory.
     */
    public function __construct()
    {
        $this->register(new TypeComparator);
        $this->register(new ScalarComparator);
        $this->register(new NumericComparator);
        $this->register(new DoubleComparator);
        $this->register(new ArrayComparator);
        $this->register(new ResourceComparator);
        $this->register(new ObjectComparator);
        $this->register(new ExceptionComparator);
        $this->register(new SplObjectStorageComparator);
        $this->register(new DOMNodeComparator);
        $this->register(new MockObjectComparator);
        $this->register(new DateTimeComparator);
    }

    /**
     * @return Factory
     */
    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new self;
        }

        return self::$instance;
    }

    /**
     * Returns the correct comparator for comparing two values.
     *
     * @param  mixed      $expected The first value to compare
     * @param  mixed      $actual   The second value to compare
     * @return Comparator
     */
    public function getComparatorFor($expected, $actual)
    {
        foreach ($this->comparators as $comparator) {
            if ($comparator->accepts($expected, $actual)) {
                return $comparator;
            }
        }
    }

    /**
     * Registers a new comparator.
     *
     * This comparator will be returned by getInstance() if its accept() method
     * returns TRUE for the compared values. It has higher priority than the
     * existing comparators, meaning that its accept() method will be tested
     * before those of the other comparators.
     *
     * @param Comparator $comparator The registered comparator
     */
    public function register(Comparator $comparator)
    {
        array_unshift($this->comparators, $comparator);

        $comparator->setFactory($this);
    }

    /**
     * Unregisters a comparator.
     *
     * This comparator will no longer be returned by getInstance().
     *
     * @param Comparator $comparator The unregistered comparator
     */
    public function unregister(Comparator $comparator)
    {
        foreach ($this->comparators as $key => $_comparator) {
            if ($comparator === $_comparator) {
                unset($this->comparators[$key]);
            }
        }
    }
}
<?php
/*
 * This file is part of the Comparator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Comparator;

/**
 * Compares PHPUnit_Framework_MockObject_MockObject instances for equality.
 */
class MockObjectComparator extends ObjectComparator
{
    /**
     * Returns whether the comparator can compare two values.
     *
     * @param  mixed $expected The first value to compare
     * @param  mixed $actual   The second value to compare
     * @return bool
     */
    public function accepts($expected, $actual)
    {
        return $expected instanceof \PHPUnit_Framework_MockObject_MockObject && $actual instanceof \PHPUnit_Framework_MockObject_MockObject;
    }

    /**
     * Converts an object to an array containing all of its private, protected
     * and public properties.
     *
     * @param  object $object
     * @return array
     */
    protected function toArray($object)
    {
        $array = parent::toArray($object);

        unset($array['__phpunit_invocationMocker']);

        return $array;
    }
}<?php
/*
 * This file is part of the Comparator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Comparator;

/**
 * Compares numerical values for equality.
 */
class NumericComparator extends ScalarComparator
{
    /**
     * Returns whether the comparator can compare two values.
     *
     * @param  mixed $expected The first value to compare
     * @param  mixed $actual   The second value to compare
     * @return bool
     */
    public function accepts($expected, $actual)
    {
        // all numerical values, but not if one of them is a double
        // or both of them are strings
        return is_numeric($expected) && is_numeric($actual) &&
               !(is_double($expected) || is_double($actual)) &&
               !(is_string($expected) && is_string($actual));
    }

    /**
     * Asserts that two values are equal.
     *
     * @param mixed $expected     First value to compare
     * @param mixed $actual       Second value to compare
     * @param float $delta        Allowed numerical distance between two values to consider them equal
     * @param bool  $canonicalize Arrays are sorted before comparison when set to true
     * @param bool  $ignoreCase   Case is ignored when set to true
     *
     * @throws ComparisonFailure
     */
    public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
    {
        if (is_infinite($actual) && is_infinite($expected)) {
            return;
        }

        if ((is_infinite($actual) xor is_infinite($expected)) ||
            (is_nan($actual) or is_nan($expected)) ||
            abs($actual - $expected) > $delta) {
            throw new ComparisonFailure(
                $expected,
                $actual,
                '',
                '',
                false,
                sprintf(
                    'Failed asserting that %s matches expected %s.',
                    $this->exporter->export($actual),
                    $this->exporter->export($expected)
                )
            );
        }
    }
}
<?php
/*
 * This file is part of the Comparator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Comparator;

/**
 * Compares objects for equality.
 */
class ObjectComparator extends ArrayComparator
{
    /**
     * Returns whether the comparator can compare two values.
     *
     * @param  mixed $expected The first value to compare
     * @param  mixed $actual   The second value to compare
     * @return bool
     */
    public function accepts($expected, $actual)
    {
        return is_object($expected) && is_object($actual);
    }

    /**
     * Asserts that two values are equal.
     *
     * @param mixed $expected     First value to compare
     * @param mixed $actual       Second value to compare
     * @param float $delta        Allowed numerical distance between two values to consider them equal
     * @param bool  $canonicalize Arrays are sorted before comparison when set to true
     * @param bool  $ignoreCase   Case is ignored when set to true
     * @param array $processed    List of already processed elements (used to prevent infinite recursion)
     *
     * @throws ComparisonFailure
     */
    public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array())
    {
        if (get_class($actual) !== get_class($expected)) {
            throw new ComparisonFailure(
                $expected,
                $actual,
                $this->exporter->export($expected),
                $this->exporter->export($actual),
                false,
                sprintf(
                    '%s is not instance of expected class "%s".',
                    $this->exporter->export($actual),
                    get_class($expected)
                )
            );
        }

        // don't compare twice to allow for cyclic dependencies
        if (in_array(array($actual, $expected), $processed, true) ||
            in_array(array($expected, $actual), $processed, true)) {
            return;
        }

        $processed[] = array($actual, $expected);

        // don't compare objects if they are identical
        // this helps to avoid the error "maximum function nesting level reached"
        // CAUTION: this conditional clause is not tested
        if ($actual !== $expected) {
            try {
                parent::assertEquals(
                    $this->toArray($expected),
                    $this->toArray($actual),
                    $delta,
                    $canonicalize,
                    $ignoreCase,
                    $processed
                );
            } catch (ComparisonFailure $e) {
                throw new ComparisonFailure(
                    $expected,
                    $actual,
                    // replace "Array" with "MyClass object"
                    substr_replace($e->getExpectedAsString(), get_class($expected) . ' Object', 0, 5),
                    substr_replace($e->getActualAsString(), get_class($actual) . ' Object', 0, 5),
                    false,
                    'Failed asserting that two objects are equal.'
                );
            }
        }
    }

    /**
     * Converts an object to an array containing all of its private, protected
     * and public properties.
     *
     * @param  object $object
     * @return array
     */
    protected function toArray($object)
    {
        return $this->exporter->toArray($object);
    }
}
<?php
/*
 * This file is part of the Comparator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Comparator;

/**
 * Compares resources for equality.
 */
class ResourceComparator extends Comparator
{
    /**
     * Returns whether the comparator can compare two values.
     *
     * @param  mixed $expected The first value to compare
     * @param  mixed $actual   The second value to compare
     * @return bool
     */
    public function accepts($expected, $actual)
    {
        return is_resource($expected) && is_resource($actual);
    }

    /**
     * Asserts that two values are equal.
     *
     * @param mixed $expected     First value to compare
     * @param mixed $actual       Second value to compare
     * @param float $delta        Allowed numerical distance between two values to consider them equal
     * @param bool  $canonicalize Arrays are sorted before comparison when set to true
     * @param bool  $ignoreCase   Case is ignored when set to true
     *
     * @throws ComparisonFailure
     */
    public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
    {
        if ($actual != $expected) {
            throw new ComparisonFailure(
                $expected,
                $actual,
                $this->exporter->export($expected),
                $this->exporter->export($actual)
            );
        }
    }
}
<?php
/*
 * This file is part of the Comparator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Comparator;

/**
 * Compares scalar or NULL values for equality.
 */
class ScalarComparator extends Comparator
{
    /**
     * Returns whether the comparator can compare two values.
     *
     * @param  mixed $expected The first value to compare
     * @param  mixed $actual   The second value to compare
     * @return bool
     * @since  Method available since Release 3.6.0
     */
    public function accepts($expected, $actual)
    {
        return ((is_scalar($expected) xor null === $expected) &&
               (is_scalar($actual) xor null === $actual))
               // allow comparison between strings and objects featuring __toString()
               || (is_string($expected) && is_object($actual) && method_exists($actual, '__toString'))
               || (is_object($expected) && method_exists($expected, '__toString') && is_string($actual));
    }

    /**
     * Asserts that two values are equal.
     *
     * @param mixed $expected     First value to compare
     * @param mixed $actual       Second value to compare
     * @param float $delta        Allowed numerical distance between two values to consider them equal
     * @param bool  $canonicalize Arrays are sorted before comparison when set to true
     * @param bool  $ignoreCase   Case is ignored when set to true
     *
     * @throws ComparisonFailure
     */
    public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
    {
        $expectedToCompare = $expected;
        $actualToCompare   = $actual;

        // always compare as strings to avoid strange behaviour
        // otherwise 0 == 'Foobar'
        if (is_string($expected) || is_string($actual)) {
            $expectedToCompare = (string) $expectedToCompare;
            $actualToCompare   = (string) $actualToCompare;

            if ($ignoreCase) {
                $expectedToCompare = strtolower($expectedToCompare);
                $actualToCompare   = strtolower($actualToCompare);
            }
        }

        if ($expectedToCompare != $actualToCompare) {
            if (is_string($expected) && is_string($actual)) {
                throw new ComparisonFailure(
                    $expected,
                    $actual,
                    $this->exporter->export($expected),
                    $this->exporter->export($actual),
                    false,
                    'Failed asserting that two strings are equal.'
                );
            }

            throw new ComparisonFailure(
                $expected,
                $actual,
                // no diff is required
                '',
                '',
                false,
                sprintf(
                    'Failed asserting that %s matches expected %s.',
                    $this->exporter->export($actual),
                    $this->exporter->export($expected)
                )
            );
        }
    }
}
<?php
/*
 * This file is part of the Comparator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Comparator;

/**
 * Compares \SplObjectStorage instances for equality.
 */
class SplObjectStorageComparator extends Comparator
{
    /**
     * Returns whether the comparator can compare two values.
     *
     * @param  mixed $expected The first value to compare
     * @param  mixed $actual   The second value to compare
     * @return bool
     */
    public function accepts($expected, $actual)
    {
        return $expected instanceof \SplObjectStorage && $actual instanceof \SplObjectStorage;
    }

    /**
     * Asserts that two values are equal.
     *
     * @param mixed $expected     First value to compare
     * @param mixed $actual       Second value to compare
     * @param float $delta        Allowed numerical distance between two values to consider them equal
     * @param bool  $canonicalize Arrays are sorted before comparison when set to true
     * @param bool  $ignoreCase   Case is ignored when set to true
     *
     * @throws ComparisonFailure
     */
    public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
    {
        foreach ($actual as $object) {
            if (!$expected->contains($object)) {
                throw new ComparisonFailure(
                    $expected,
                    $actual,
                    $this->exporter->export($expected),
                    $this->exporter->export($actual),
                    false,
                    'Failed asserting that two objects are equal.'
                );
            }
        }

        foreach ($expected as $object) {
            if (!$actual->contains($object)) {
                throw new ComparisonFailure(
                    $expected,
                    $actual,
                    $this->exporter->export($expected),
                    $this->exporter->export($actual),
                    false,
                    'Failed asserting that two objects are equal.'
                );
            }
        }
    }
}
<?php
/*
 * This file is part of the Comparator package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Comparator;

/**
 * Compares values for type equality.
 */
class TypeComparator extends Comparator
{
    /**
     * Returns whether the comparator can compare two values.
     *
     * @param  mixed $expected The first value to compare
     * @param  mixed $actual   The second value to compare
     * @return bool
     */
    public function accepts($expected, $actual)
    {
        return true;
    }

    /**
     * Asserts that two values are equal.
     *
     * @param mixed $expected     First value to compare
     * @param mixed $actual       Second value to compare
     * @param float $delta        Allowed numerical distance between two values to consider them equal
     * @param bool  $canonicalize Arrays are sorted before comparison when set to true
     * @param bool  $ignoreCase   Case is ignored when set to true
     *
     * @throws ComparisonFailure
     */
    public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
    {
        if (gettype($expected) != gettype($actual)) {
            throw new ComparisonFailure(
                $expected,
                $actual,
                // we don't need a diff
                '',
                '',
                false,
                sprintf(
                    '%s does not match expected type "%s".',
                    $this->exporter->shortenedExport($actual),
                    gettype($expected)
                )
            );
        }
    }
}
<?php
/*
 * This file is part of sebastian/diff.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Diff;

class Chunk
{
    /**
     * @var int
     */
    private $start;

    /**
     * @var int
     */
    private $startRange;

    /**
     * @var int
     */
    private $end;

    /**
     * @var int
     */
    private $endRange;

    /**
     * @var array
     */
    private $lines;

    /**
     * @param int   $start
     * @param int   $startRange
     * @param int   $end
     * @param int   $endRange
     * @param array $lines
     */
    public function __construct($start = 0, $startRange = 1, $end = 0, $endRange = 1, array $lines = array())
    {
        $this->start      = (int) $start;
        $this->startRange = (int) $startRange;
        $this->end        = (int) $end;
        $this->endRange   = (int) $endRange;
        $this->lines      = $lines;
    }

    /**
     * @return int
     */
    public function getStart()
    {
        return $this->start;
    }

    /**
     * @return int
     */
    public function getStartRange()
    {
        return $this->startRange;
    }

    /**
     * @return int
     */
    public function getEnd()
    {
        return $this->end;
    }

    /**
     * @return int
     */
    public function getEndRange()
    {
        return $this->endRange;
    }

    /**
     * @return array
     */
    public function getLines()
    {
        return $this->lines;
    }

    /**
     * @param array $lines
     */
    public function setLines(array $lines)
    {
        $this->lines = $lines;
    }
}
<?php
/*
 * This file is part of sebastian/diff.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Diff;

class Diff
{
    /**
     * @var string
     */
    private $from;

    /**
     * @var string
     */
    private $to;

    /**
     * @var Chunk[]
     */
    private $chunks;

    /**
     * @param string  $from
     * @param string  $to
     * @param Chunk[] $chunks
     */
    public function __construct($from, $to, array $chunks = array())
    {
        $this->from   = $from;
        $this->to     = $to;
        $this->chunks = $chunks;
    }

    /**
     * @return string
     */
    public function getFrom()
    {
        return $this->from;
    }

    /**
     * @return string
     */
    public function getTo()
    {
        return $this->to;
    }

    /**
     * @return Chunk[]
     */
    public function getChunks()
    {
        return $this->chunks;
    }

    /**
     * @param Chunk[] $chunks
     */
    public function setChunks(array $chunks)
    {
        $this->chunks = $chunks;
    }
}
<?php
/*
 * This file is part of sebastian/diff.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Diff;

use SebastianBergmann\Diff\LCS\LongestCommonSubsequence;
use SebastianBergmann\Diff\LCS\TimeEfficientImplementation;
use SebastianBergmann\Diff\LCS\MemoryEfficientImplementation;

/**
 * Diff implementation.
 */
class Differ
{
    /**
     * @var string
     */
    private $header;

    /**
     * @var bool
     */
    private $showNonDiffLines;

    /**
     * @param string $header
     * @param bool   $showNonDiffLines
     */
    public function __construct($header = "--- Original\n+++ New\n", $showNonDiffLines = true)
    {
        $this->header           = $header;
        $this->showNonDiffLines = $showNonDiffLines;
    }

    /**
     * Returns the diff between two arrays or strings as string.
     *
     * @param array|string             $from
     * @param array|string             $to
     * @param LongestCommonSubsequence $lcs
     *
     * @return string
     */
    public function diff($from, $to, LongestCommonSubsequence $lcs = null)
    {
        $from  = $this->validateDiffInput($from);
        $to    = $this->validateDiffInput($to);
        $diff  = $this->diffToArray($from, $to, $lcs);
        $old   = $this->checkIfDiffInOld($diff);
        $start = isset($old[0]) ? $old[0] : 0;
        $end   = \count($diff);

        if ($tmp = \array_search($end, $old)) {
            $end = $tmp;
        }

        return $this->getBuffer($diff, $old, $start, $end);
    }

    /**
     * Casts variable to string if it is not a string or array.
     *
     * @param mixed $input
     *
     * @return string
     */
    private function validateDiffInput($input)
    {
        if (!\is_array($input) && !\is_string($input)) {
            return (string) $input;
        }

        return $input;
    }

    /**
     * Takes input of the diff array and returns the old array.
     * Iterates through diff line by line,
     *
     * @param array $diff
     *
     * @return array
     */
    private function checkIfDiffInOld(array $diff)
    {
        $inOld = false;
        $i     = 0;
        $old   = array();

        foreach ($diff as $line) {
            if ($line[1] === 0 /* OLD */) {
                if ($inOld === false) {
                    $inOld = $i;
                }
            } elseif ($inOld !== false) {
                if (($i - $inOld) > 5) {
                    $old[$inOld] = $i - 1;
                }

                $inOld = false;
            }

            ++$i;
        }

        return $old;
    }

    /**
     * Generates buffer in string format, returning the patch.
     *
     * @param array $diff
     * @param array $old
     * @param int   $start
     * @param int   $end
     *
     * @return string
     */
    private function getBuffer(array $diff, array $old, $start, $end)
    {
        $buffer = $this->header;

        if (!isset($old[$start])) {
            $buffer = $this->getDiffBufferElementNew($diff, $buffer, $start);
            ++$start;
        }

        for ($i = $start; $i < $end; $i++) {
            if (isset($old[$i])) {
                $i      = $old[$i];
                $buffer = $this->getDiffBufferElementNew($diff, $buffer, $i);
            } else {
                $buffer = $this->getDiffBufferElement($diff, $buffer, $i);
            }
        }

        return $buffer;
    }

    /**
     * Gets individual buffer element.
     *
     * @param array  $diff
     * @param string $buffer
     * @param int    $diffIndex
     *
     * @return string
     */
    private function getDiffBufferElement(array $diff, $buffer, $diffIndex)
    {
        if ($diff[$diffIndex][1] === 1 /* ADDED */) {
            $buffer .= '+' . $diff[$diffIndex][0] . "\n";
        } elseif ($diff[$diffIndex][1] === 2 /* REMOVED */) {
            $buffer .= '-' . $diff[$diffIndex][0] . "\n";
        } elseif ($this->showNonDiffLines === true) {
            $buffer .= ' ' . $diff[$diffIndex][0] . "\n";
        }

        return $buffer;
    }

    /**
     * Gets individual buffer element with opening.
     *
     * @param array  $diff
     * @param string $buffer
     * @param int    $diffIndex
     *
     * @return string
     */
    private function getDiffBufferElementNew(array $diff, $buffer, $diffIndex)
    {
        if ($this->showNonDiffLines === true) {
            $buffer .= "@@ @@\n";
        }

        return $this->getDiffBufferElement($diff, $buffer, $diffIndex);
    }

    /**
     * Returns the diff between two arrays or strings as array.
     *
     * Each array element contains two elements:
     *   - [0] => mixed $token
     *   - [1] => 2|1|0
     *
     * - 2: REMOVED: $token was removed from $from
     * - 1: ADDED: $token was added to $from
     * - 0: OLD: $token is not changed in $to
     *
     * @param array|string             $from
     * @param array|string             $to
     * @param LongestCommonSubsequence $lcs
     *
     * @return array
     */
    public function diffToArray($from, $to, LongestCommonSubsequence $lcs = null)
    {
        if (\is_string($from)) {
            $fromMatches = $this->getNewLineMatches($from);
            $from        = $this->splitStringByLines($from);
        } elseif (\is_array($from)) {
            $fromMatches = array();
        } else {
            throw new \InvalidArgumentException('"from" must be an array or string.');
        }

        if (\is_string($to)) {
            $toMatches = $this->getNewLineMatches($to);
            $to        = $this->splitStringByLines($to);
        } elseif (\is_array($to)) {
            $toMatches = array();
        } else {
            throw new \InvalidArgumentException('"to" must be an array or string.');
        }

        list($from, $to, $start, $end) = self::getArrayDiffParted($from, $to);

        if ($lcs === null) {
            $lcs = $this->selectLcsImplementation($from, $to);
        }

        $common = $lcs->calculate(\array_values($from), \array_values($to));
        $diff   = array();

        if ($this->detectUnmatchedLineEndings($fromMatches, $toMatches)) {
            $diff[] = array(
                '#Warning: Strings contain different line endings!',
                0
            );
        }

        foreach ($start as $token) {
            $diff[] = array($token, 0 /* OLD */);
        }

        \reset($from);
        \reset($to);

        foreach ($common as $token) {
            while (($fromToken = \reset($from)) !== $token) {
                $diff[] = array(\array_shift($from), 2 /* REMOVED */);
            }

            while (($toToken = \reset($to)) !== $token) {
                $diff[] = array(\array_shift($to), 1 /* ADDED */);
            }

            $diff[] = array($token, 0 /* OLD */);

            \array_shift($from);
            \array_shift($to);
        }

        while (($token = \array_shift($from)) !== null) {
            $diff[] = array($token, 2 /* REMOVED */);
        }

        while (($token = \array_shift($to)) !== null) {
            $diff[] = array($token, 1 /* ADDED */);
        }

        foreach ($end as $token) {
            $diff[] = array($token, 0 /* OLD */);
        }

        return $diff;
    }

    /**
     * Get new strings denoting new lines from a given string.
     *
     * @param string $string
     *
     * @return array
     */
    private function getNewLineMatches($string)
    {
        \preg_match_all('(\r\n|\r|\n)', $string, $stringMatches);

        return $stringMatches;
    }

    /**
     * Checks if input is string, if so it will split it line-by-line.
     *
     * @param string $input
     *
     * @return array
     */
    private function splitStringByLines($input)
    {
        return \preg_split('(\r\n|\r|\n)', $input);
    }

    /**
     * @param array $from
     * @param array $to
     *
     * @return LongestCommonSubsequence
     */
    private function selectLcsImplementation(array $from, array $to)
    {
        // We do not want to use the time-efficient implementation if its memory
        // footprint will probably exceed this value. Note that the footprint
        // calculation is only an estimation for the matrix and the LCS method
        // will typically allocate a bit more memory than this.
        $memoryLimit = 100 * 1024 * 1024;

        if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) {
            return new MemoryEfficientImplementation;
        }

        return new TimeEfficientImplementation;
    }

    /**
     * Calculates the estimated memory footprint for the DP-based method.
     *
     * @param array $from
     * @param array $to
     *
     * @return int|float
     */
    private function calculateEstimatedFootprint(array $from, array $to)
    {
        $itemSize = PHP_INT_SIZE === 4 ? 76 : 144;

        return $itemSize * \pow(\min(\count($from), \count($to)), 2);
    }

    /**
     * Returns true if line ends don't match on fromMatches and toMatches.
     *
     * @param array $fromMatches
     * @param array $toMatches
     *
     * @return bool
     */
    private function detectUnmatchedLineEndings(array $fromMatches, array $toMatches)
    {
        return isset($fromMatches[0], $toMatches[0]) &&
               \count($fromMatches[0]) === \count($toMatches[0]) &&
               $fromMatches[0] !== $toMatches[0];
    }

    /**
     * @param array $from
     * @param array $to
     *
     * @return array
     */
    private static function getArrayDiffParted(array &$from, array &$to)
    {
        $start = array();
        $end   = array();

        \reset($to);

        foreach ($from as $k => $v) {
            $toK = \key($to);

            if ($toK === $k && $v === $to[$k]) {
                $start[$k] = $v;

                unset($from[$k], $to[$k]);
            } else {
                break;
            }
        }

        \end($from);
        \end($to);

        do {
            $fromK = \key($from);
            $toK   = \key($to);

            if (null === $fromK || null === $toK || \current($from) !== \current($to)) {
                break;
            }

            \prev($from);
            \prev($to);

            $end = array($fromK => $from[$fromK]) + $end;
            unset($from[$fromK], $to[$toK]);
        } while (true);

        return array($from, $to, $start, $end);
    }
}
<?php
/*
 * This file is part of sebastian/diff.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Diff\LCS;

/**
 * Interface for implementations of longest common subsequence calculation.
 */
interface LongestCommonSubsequence
{
    /**
     * Calculates the longest common subsequence of two arrays.
     *
     * @param array $from
     * @param array $to
     *
     * @return array
     */
    public function calculate(array $from, array $to);
}
<?php
/*
 * This file is part of sebastian/diff.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Diff\LCS;

/**
 * Memory-efficient implementation of longest common subsequence calculation.
 */
class MemoryEfficientImplementation implements LongestCommonSubsequence
{
    /**
     * Calculates the longest common subsequence of two arrays.
     *
     * @param array $from
     * @param array $to
     *
     * @return array
     */
    public function calculate(array $from, array $to)
    {
        $cFrom = \count($from);
        $cTo   = \count($to);

        if ($cFrom === 0) {
            return array();
        }

        if ($cFrom === 1) {
            if (\in_array($from[0], $to, true)) {
                return array($from[0]);
            }

            return array();
        }

        $i         = (int) ($cFrom / 2);
        $fromStart = \array_slice($from, 0, $i);
        $fromEnd   = \array_slice($from, $i);
        $llB       = $this->length($fromStart, $to);
        $llE       = $this->length(\array_reverse($fromEnd), \array_reverse($to));
        $jMax      = 0;
        $max       = 0;

        for ($j = 0; $j <= $cTo; $j++) {
            $m = $llB[$j] + $llE[$cTo - $j];

            if ($m >= $max) {
                $max  = $m;
                $jMax = $j;
            }
        }

        $toStart = \array_slice($to, 0, $jMax);
        $toEnd   = \array_slice($to, $jMax);

        return \array_merge(
            $this->calculate($fromStart, $toStart),
            $this->calculate($fromEnd, $toEnd)
        );
    }

    /**
     * @param array $from
     * @param array $to
     *
     * @return array
     */
    private function length(array $from, array $to)
    {
        $current = \array_fill(0, \count($to) + 1, 0);
        $cFrom   = \count($from);
        $cTo     = \count($to);

        for ($i = 0; $i < $cFrom; $i++) {
            $prev = $current;

            for ($j = 0; $j < $cTo; $j++) {
                if ($from[$i] === $to[$j]) {
                    $current[$j + 1] = $prev[$j] + 1;
                } else {
                    $current[$j + 1] = \max($current[$j], $prev[$j + 1]);
                }
            }
        }

        return $current;
    }
}
<?php
/*
 * This file is part of sebastian/diff.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Diff\LCS;

/**
 * Time-efficient implementation of longest common subsequence calculation.
 */
class TimeEfficientImplementation implements LongestCommonSubsequence
{
    /**
     * Calculates the longest common subsequence of two arrays.
     *
     * @param array $from
     * @param array $to
     *
     * @return array
     */
    public function calculate(array $from, array $to)
    {
        $common     = array();
        $fromLength = \count($from);
        $toLength   = \count($to);
        $width      = $fromLength + 1;
        $matrix     = new \SplFixedArray($width * ($toLength + 1));

        for ($i = 0; $i <= $fromLength; ++$i) {
            $matrix[$i] = 0;
        }

        for ($j = 0; $j <= $toLength; ++$j) {
            $matrix[$j * $width] = 0;
        }

        for ($i = 1; $i <= $fromLength; ++$i) {
            for ($j = 1; $j <= $toLength; ++$j) {
                $o          = ($j * $width) + $i;
                $matrix[$o] = \max(
                    $matrix[$o - 1],
                    $matrix[$o - $width],
                    $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0
                );
            }
        }

        $i = $fromLength;
        $j = $toLength;

        while ($i > 0 && $j > 0) {
            if ($from[$i - 1] === $to[$j - 1]) {
                $common[] = $from[$i - 1];
                --$i;
                --$j;
            } else {
                $o = ($j * $width) + $i;

                if ($matrix[$o - $width] > $matrix[$o - 1]) {
                    --$j;
                } else {
                    --$i;
                }
            }
        }

        return \array_reverse($common);
    }
}
<?php
/*
 * This file is part of sebastian/diff.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Diff;

class Line
{
    const ADDED     = 1;
    const REMOVED   = 2;
    const UNCHANGED = 3;

    /**
     * @var int
     */
    private $type;

    /**
     * @var string
     */
    private $content;

    /**
     * @param int    $type
     * @param string $content
     */
    public function __construct($type = self::UNCHANGED, $content = '')
    {
        $this->type    = $type;
        $this->content = $content;
    }

    /**
     * @return string
     */
    public function getContent()
    {
        return $this->content;
    }

    /**
     * @return int
     */
    public function getType()
    {
        return $this->type;
    }
}
<?php
/*
 * This file is part of sebastian/diff.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Diff;

/**
 * Unified diff parser.
 */
class Parser
{
    /**
     * @param string $string
     *
     * @return Diff[]
     */
    public function parse($string)
    {
        $lines = \preg_split('(\r\n|\r|\n)', $string);

        if (!empty($lines) && $lines[\count($lines) - 1] == '') {
            \array_pop($lines);
        }

        $lineCount = \count($lines);
        $diffs     = array();
        $diff      = null;
        $collected = array();

        for ($i = 0; $i < $lineCount; ++$i) {
            if (\preg_match('(^---\\s+(?P<file>\\S+))', $lines[$i], $fromMatch) &&
                \preg_match('(^\\+\\+\\+\\s+(?P<file>\\S+))', $lines[$i + 1], $toMatch)) {
                if ($diff !== null) {
                    $this->parseFileDiff($diff, $collected);

                    $diffs[]   = $diff;
                    $collected = array();
                }

                $diff = new Diff($fromMatch['file'], $toMatch['file']);

                ++$i;
            } else {
                if (\preg_match('/^(?:diff --git |index [\da-f\.]+|[+-]{3} [ab])/', $lines[$i])) {
                    continue;
                }

                $collected[] = $lines[$i];
            }
        }

        if ($diff !== null && \count($collected)) {
            $this->parseFileDiff($diff, $collected);

            $diffs[] = $diff;
        }

        return $diffs;
    }

    /**
     * @param Diff  $diff
     * @param array $lines
     */
    private function parseFileDiff(Diff $diff, array $lines)
    {
        $chunks = array();
        $chunk  = null;

        foreach ($lines as $line) {
            if (\preg_match('/^@@\s+-(?P<start>\d+)(?:,\s*(?P<startrange>\d+))?\s+\+(?P<end>\d+)(?:,\s*(?P<endrange>\d+))?\s+@@/', $line, $match)) {
                $chunk = new Chunk(
                    $match['start'],
                    isset($match['startrange']) ? \max(1, $match['startrange']) : 1,
                    $match['end'],
                    isset($match['endrange']) ? \max(1, $match['endrange']) : 1
                );

                $chunks[]  = $chunk;
                $diffLines = array();

                continue;
            }

            if (\preg_match('/^(?P<type>[+ -])?(?P<line>.*)/', $line, $match)) {
                $type = Line::UNCHANGED;

                if ($match['type'] === '+') {
                    $type = Line::ADDED;
                } elseif ($match['type'] === '-') {
                    $type = Line::REMOVED;
                }

                $diffLines[] = new Line($type, $match['line']);

                if (null !== $chunk) {
                    $chunk->setLines($diffLines);
                }
            }
        }

        $diff->setChunks($chunks);
    }
}
<?php
/*
 * This file is part of the Environment package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Environment;

/**
 */
class Console
{
    const STDIN  = 0;
    const STDOUT = 1;
    const STDERR = 2;

    /**
     * Returns true if STDOUT supports colorization.
     *
     * This code has been copied and adapted from
     * Symfony\Component\Console\Output\OutputStream.
     *
     * @return bool
     */
    public function hasColorSupport()
    {
        if (DIRECTORY_SEPARATOR == '\\') {
            return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM');
        }

        if (!defined('STDOUT')) {
            return false;
        }

        return $this->isInteractive(STDOUT);
    }

    /**
     * Returns the number of columns of the terminal.
     *
     * @return int
     */
    public function getNumberOfColumns()
    {
        if (DIRECTORY_SEPARATOR == '\\') {
            $columns = 80;

            if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) {
                $columns = $matches[1];
            } elseif (function_exists('proc_open')) {
                $process = proc_open(
                    'mode CON',
                    [
                        1 => ['pipe', 'w'],
                        2 => ['pipe', 'w']
                    ],
                    $pipes,
                    null,
                    null,
                    ['suppress_errors' => true]
                );

                if (is_resource($process)) {
                    $info = stream_get_contents($pipes[1]);

                    fclose($pipes[1]);
                    fclose($pipes[2]);
                    proc_close($process);

                    if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
                        $columns = $matches[2];
                    }
                }
            }

            return $columns - 1;
        }

        if (!$this->isInteractive(self::STDIN)) {
            return 80;
        }

        if (function_exists('shell_exec') && preg_match('#\d+ (\d+)#', shell_exec('stty size'), $match) === 1) {
            if ((int) $match[1] > 0) {
                return (int) $match[1];
            }
        }

        if (function_exists('shell_exec') && preg_match('#columns = (\d+);#', shell_exec('stty'), $match) === 1) {
            if ((int) $match[1] > 0) {
                return (int) $match[1];
            }
        }

        return 80;
    }

    /**
     * Returns if the file descriptor is an interactive terminal or not.
     *
     * @param int|resource $fileDescriptor
     *
     * @return bool
     */
    public function isInteractive($fileDescriptor = self::STDOUT)
    {
        return function_exists('posix_isatty') && @posix_isatty($fileDescriptor);
    }
}
<?php
/*
 * This file is part of the Environment package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Environment;

/**
 * Utility class for HHVM/PHP environment handling.
 */
class Runtime
{
    /**
     * @var string
     */
    private static $binary;

    /**
     * Returns true when Xdebug is supported or
     * the runtime used is PHPDBG (PHP >= 7.0).
     *
     * @return bool
     */
    public function canCollectCodeCoverage()
    {
        return $this->hasXdebug() || $this->hasPHPDBGCodeCoverage();
    }

    /**
     * Returns the path to the binary of the current runtime.
     * Appends ' --php' to the path when the runtime is HHVM.
     *
     * @return string
     */
    public function getBinary()
    {
        // HHVM
        if (self::$binary === null && $this->isHHVM()) {
            if ((self::$binary = getenv('PHP_BINARY')) === false) {
                self::$binary = PHP_BINARY;
            }

            self::$binary = escapeshellarg(self::$binary) . ' --php';
        }

        // PHP >= 5.4.0
        if (self::$binary === null && defined('PHP_BINARY')) {
            if (PHP_BINARY !== '') {
                self::$binary = escapeshellarg(PHP_BINARY);
            }
        }

        // PHP < 5.4.0
        if (self::$binary === null) {
            if (PHP_SAPI == 'cli' && isset($_SERVER['_'])) {
                if (strpos($_SERVER['_'], 'phpunit') !== false) {
                    $file = file($_SERVER['_']);

                    if (strpos($file[0], ' ') !== false) {
                        $tmp          = explode(' ', $file[0]);
                        self::$binary = escapeshellarg(trim($tmp[1]));
                    } else {
                        self::$binary = escapeshellarg(ltrim(trim($file[0]), '#!'));
                    }
                } elseif (strpos(basename($_SERVER['_']), 'php') !== false) {
                    self::$binary = escapeshellarg($_SERVER['_']);
                }
            }
        }

        if (self::$binary === null) {
            $possibleBinaryLocations = [
                PHP_BINDIR . '/php',
                PHP_BINDIR . '/php-cli.exe',
                PHP_BINDIR . '/php.exe'
            ];

            foreach ($possibleBinaryLocations as $binary) {
                if (is_readable($binary)) {
                    self::$binary = escapeshellarg($binary);
                    break;
                }
            }
        }

        if (self::$binary === null) {
            self::$binary = 'php';
        }

        return self::$binary;
    }

    /**
     * @return string
     */
    public function getNameWithVersion()
    {
        return $this->getName() . ' ' . $this->getVersion();
    }

    /**
     * @return string
     */
    public function getName()
    {
        if ($this->isHHVM()) {
            return 'HHVM';
        } elseif ($this->isPHPDBG()) {
            return 'PHPDBG';
        } else {
            return 'PHP';
        }
    }

    /**
     * @return string
     */
    public function getVendorUrl()
    {
        if ($this->isHHVM()) {
            return 'http://hhvm.com/';
        } else {
            return 'https://secure.php.net/';
        }
    }

    /**
     * @return string
     */
    public function getVersion()
    {
        if ($this->isHHVM()) {
            return HHVM_VERSION;
        } else {
            return PHP_VERSION;
        }
    }

    /**
     * Returns true when the runtime used is PHP and Xdebug is loaded.
     *
     * @return bool
     */
    public function hasXdebug()
    {
        return ($this->isPHP() || $this->isHHVM()) && extension_loaded('xdebug');
    }

    /**
     * Returns true when the runtime used is HHVM.
     *
     * @return bool
     */
    public function isHHVM()
    {
        return defined('HHVM_VERSION');
    }

    /**
     * Returns true when the runtime used is PHP without the PHPDBG SAPI.
     *
     * @return bool
     */
    public function isPHP()
    {
        return !$this->isHHVM() && !$this->isPHPDBG();
    }

    /**
     * Returns true when the runtime used is PHP with the PHPDBG SAPI.
     *
     * @return bool
     */
    public function isPHPDBG()
    {
        return PHP_SAPI === 'phpdbg' && !$this->isHHVM();
    }

    /**
     * Returns true when the runtime used is PHP with the PHPDBG SAPI
     * and the phpdbg_*_oplog() functions are available (PHP >= 7.0).
     *
     * @return bool
     */
    public function hasPHPDBGCodeCoverage()
    {
        return $this->isPHPDBG() && function_exists('phpdbg_start_oplog');
    }
}
<?php
/*
 * This file is part of the Exporter package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\Exporter;

use SebastianBergmann\RecursionContext\Context;

/**
 * A nifty utility for visualizing PHP variables.
 *
 * <code>
 * <?php
 * use SebastianBergmann\Exporter\Exporter;
 *
 * $exporter = new Exporter;
 * print $exporter->export(new Exception);
 * </code>
 */
class Exporter
{
    /**
     * Exports a value as a string
     *
     * The output of this method is similar to the output of print_r(), but
     * improved in various aspects:
     *
     *  - NULL is rendered as "null" (instead of "")
     *  - TRUE is rendered as "true" (instead of "1")
     *  - FALSE is rendered as "false" (instead of "")
     *  - Strings are always quoted with single quotes
     *  - Carriage returns and newlines are normalized to \n
     *  - Recursion and repeated rendering is treated properly
     *
     * @param  mixed  $value
     * @param  int    $indentation The indentation level of the 2nd+ line
     * @return string
     */
    public function export($value, $indentation = 0)
    {
        return $this->recursiveExport($value, $indentation);
    }

    /**
     * @param  mixed   $data
     * @param  Context $context
     * @return string
     */
    public function shortenedRecursiveExport(&$data, Context $context = null)
    {
        $result   = array();
        $exporter = new self();

        if (!$context) {
            $context = new Context;
        }

        $array = $data;
        $context->add($data);

        foreach ($array as $key => $value) {
            if (is_array($value)) {
                if ($context->contains($data[$key]) !== false) {
                    $result[] = '*RECURSION*';
                }

                else {
                    $result[] = sprintf(
                        'array(%s)',
                        $this->shortenedRecursiveExport($data[$key], $context)
                    );
                }
            }

            else {
                $result[] = $exporter->shortenedExport($value);
            }
        }

        return implode(', ', $result);
    }

    /**
     * Exports a value into a single-line string
     *
     * The output of this method is similar to the output of
     * SebastianBergmann\Exporter\Exporter::export().
     *
     * Newlines are replaced by the visible string '\n'.
     * Contents of arrays and objects (if any) are replaced by '...'.
     *
     * @param  mixed  $value
     * @return string
     * @see    SebastianBergmann\Exporter\Exporter::export
     */
    public function shortenedExport($value)
    {
        if (is_string($value)) {
            $string = $this->export($value);

            if (function_exists('mb_strlen')) {
                if (mb_strlen($string) > 40) {
                    $string = mb_substr($string, 0, 30) . '...' . mb_substr($string, -7);
                }
            } else {
                if (strlen($string) > 40) {
                    $string = substr($string, 0, 30) . '...' . substr($string, -7);
                }
            }

            return str_replace("\n", '\n', $string);
        }

        if (is_object($value)) {
            return sprintf(
                '%s Object (%s)',
                get_class($value),
                count($this->toArray($value)) > 0 ? '...' : ''
            );
        }

        if (is_array($value)) {
            return sprintf(
                'Array (%s)',
                count($value) > 0 ? '...' : ''
            );
        }

        return $this->export($value);
    }

    /**
     * Converts an object to an array containing all of its private, protected
     * and public properties.
     *
     * @param  mixed $value
     * @return array
     */
    public function toArray($value)
    {
        if (!is_object($value)) {
            return (array) $value;
        }

        $array = array();

        foreach ((array) $value as $key => $val) {
            // properties are transformed to keys in the following way:
            // private   $property => "\0Classname\0property"
            // protected $property => "\0*\0property"
            // public    $property => "property"
            if (preg_match('/^\0.+\0(.+)$/', $key, $matches)) {
                $key = $matches[1];
            }

            // See https://github.com/php/php-src/commit/5721132
            if ($key === "\0gcdata") {
                continue;
            }

            $array[$key] = $val;
        }

        // Some internal classes like SplObjectStorage don't work with the
        // above (fast) mechanism nor with reflection in Zend.
        // Format the output similarly to print_r() in this case
        if ($value instanceof \SplObjectStorage) {
            // However, the fast method does work in HHVM, and exposes the
            // internal implementation. Hide it again.
            if (property_exists('\SplObjectStorage', '__storage')) {
                unset($array['__storage']);
            } elseif (property_exists('\SplObjectStorage', 'storage')) {
                unset($array['storage']);
            }

            if (property_exists('\SplObjectStorage', '__key')) {
                unset($array['__key']);
            }

            foreach ($value as $key => $val) {
                $array[spl_object_hash($val)] = array(
                    'obj' => $val,
                    'inf' => $value->getInfo(),
                );
            }
        }

        return $array;
    }

    /**
     * Recursive implementation of export
     *
     * @param  mixed                                       $value       The value to export
     * @param  int                                         $indentation The indentation level of the 2nd+ line
     * @param  \SebastianBergmann\RecursionContext\Context $processed   Previously processed objects
     * @return string
     * @see    SebastianBergmann\Exporter\Exporter::export
     */
    protected function recursiveExport(&$value, $indentation, $processed = null)
    {
        if ($value === null) {
            return 'null';
        }

        if ($value === true) {
            return 'true';
        }

        if ($value === false) {
            return 'false';
        }

        if (is_float($value) && floatval(intval($value)) === $value) {
            return "$value.0";
        }

        if (is_resource($value)) {
            return sprintf(
                'resource(%d) of type (%s)',
                $value,
                get_resource_type($value)
            );
        }

        if (is_string($value)) {
            // Match for most non printable chars somewhat taking multibyte chars into account
            if (preg_match('/[^\x09-\x0d\x1b\x20-\xff]/', $value)) {
                return 'Binary String: 0x' . bin2hex($value);
            }

            return "'" .
            str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) .
            "'";
        }

        $whitespace = str_repeat(' ', 4 * $indentation);

        if (!$processed) {
            $processed = new Context;
        }

        if (is_array($value)) {
            if (($key = $processed->contains($value)) !== false) {
                return 'Array &' . $key;
            }

            $array  = $value;
            $key    = $processed->add($value);
            $values = '';

            if (count($array) > 0) {
                foreach ($array as $k => $v) {
                    $values .= sprintf(
                        '%s    %s => %s' . "\n",
                        $whitespace,
                        $this->recursiveExport($k, $indentation),
                        $this->recursiveExport($value[$k], $indentation + 1, $processed)
                    );
                }

                $values = "\n" . $values . $whitespace;
            }

            return sprintf('Array &%s (%s)', $key, $values);
        }

        if (is_object($value)) {
            $class = get_class($value);

            if ($hash = $processed->contains($value)) {
                return sprintf('%s Object &%s', $class, $hash);
            }

            $hash   = $processed->add($value);
            $values = '';
            $array  = $this->toArray($value);

            if (count($array) > 0) {
                foreach ($array as $k => $v) {
                    $values .= sprintf(
                        '%s    %s => %s' . "\n",
                        $whitespace,
                        $this->recursiveExport($k, $indentation),
                        $this->recursiveExport($v, $indentation + 1, $processed)
                    );
                }

                $values = "\n" . $values . $whitespace;
            }

            return sprintf('%s Object &%s (%s)', $class, $hash, $values);
        }

        return var_export($value, true);
    }
}
<?php
/*
 * This file is part of the GlobalState package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\GlobalState;

use ReflectionClass;

/**
 * A blacklist for global state elements that should not be snapshotted.
 */
class Blacklist
{
    /**
     * @var array
     */
    private $globalVariables = array();

    /**
     * @var array
     */
    private $classes = array();

    /**
     * @var array
     */
    private $classNamePrefixes = array();

    /**
     * @var array
     */
    private $parentClasses = array();

    /**
     * @var array
     */
    private $interfaces = array();

    /**
     * @var array
     */
    private $staticAttributes = array();

    /**
     * @param string $variableName
     */
    public function addGlobalVariable($variableName)
    {
        $this->globalVariables[$variableName] = true;
    }

    /**
     * @param string $className
     */
    public function addClass($className)
    {
        $this->classes[] = $className;
    }

    /**
     * @param string $className
     */
    public function addSubclassesOf($className)
    {
        $this->parentClasses[] = $className;
    }

    /**
     * @param string $interfaceName
     */
    public function addImplementorsOf($interfaceName)
    {
        $this->interfaces[] = $interfaceName;
    }

    /**
     * @param string $classNamePrefix
     */
    public function addClassNamePrefix($classNamePrefix)
    {
        $this->classNamePrefixes[] = $classNamePrefix;
    }

    /**
     * @param string $className
     * @param string $attributeName
     */
    public function addStaticAttribute($className, $attributeName)
    {
        if (!isset($this->staticAttributes[$className])) {
            $this->staticAttributes[$className] = array();
        }

        $this->staticAttributes[$className][$attributeName] = true;
    }

    /**
     * @param  string $variableName
     * @return bool
     */
    public function isGlobalVariableBlacklisted($variableName)
    {
        return isset($this->globalVariables[$variableName]);
    }

    /**
     * @param  string $className
     * @param  string $attributeName
     * @return bool
     */
    public function isStaticAttributeBlacklisted($className, $attributeName)
    {
        if (in_array($className, $this->classes)) {
            return true;
        }

        foreach ($this->classNamePrefixes as $prefix) {
            if (strpos($className, $prefix) === 0) {
                return true;
            }
        }

        $class = new ReflectionClass($className);

        foreach ($this->parentClasses as $type) {
            if ($class->isSubclassOf($type)) {
                return true;
            }
        }

        foreach ($this->interfaces as $type) {
            if ($class->implementsInterface($type)) {
                return true;
            }
        }

        if (isset($this->staticAttributes[$className][$attributeName])) {
            return true;
        }

        return false;
    }
}
<?php
/*
 * This file is part of the GlobalState package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\GlobalState;

/**
 * Exports parts of a Snapshot as PHP code.
 */
class CodeExporter
{
    /**
     * @param  Snapshot $snapshot
     * @return string
     */
    public function constants(Snapshot $snapshot)
    {
        $result = '';

        foreach ($snapshot->constants() as $name => $value) {
            $result .= sprintf(
                'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n",
                $name,
                $name,
                $this->exportVariable($value)
            );
        }

        return $result;
    }

    /**
     * @param  Snapshot $snapshot
     * @return string
     */
    public function iniSettings(Snapshot $snapshot)
    {
        $result = '';

        foreach ($snapshot->iniSettings() as $key => $value) {
            $result .= sprintf(
                '@ini_set(%s, %s);' . "\n",
                $this->exportVariable($key),
                $this->exportVariable($value)
            );
        }

        return $result;
    }

    /**
     * @param  mixed  $variable
     * @return string
     */
    private function exportVariable($variable)
    {
        if (is_scalar($variable) || is_null($variable) ||
            (is_array($variable) && $this->arrayOnlyContainsScalars($variable))) {
            return var_export($variable, true);
        }

        return 'unserialize(' . var_export(serialize($variable), true) . ')';
    }

    /**
     * @param  array $array
     * @return bool
     */
    private function arrayOnlyContainsScalars(array $array)
    {
        $result = true;

        foreach ($array as $element) {
            if (is_array($element)) {
                $result = self::arrayOnlyContainsScalars($element);
            } elseif (!is_scalar($element) && !is_null($element)) {
                $result = false;
            }

            if ($result === false) {
                break;
            }
        }

        return $result;
    }
}
<?php
/*
 * This file is part of the GlobalState package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\GlobalState;

/**
 */
interface Exception
{
}
<?php
/*
 * This file is part of the GlobalState package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\GlobalState;

use ReflectionProperty;

/**
 * Restorer of snapshots of global state.
 */
class Restorer
{
    /**
     * Deletes function definitions that are not defined in a snapshot.
     *
     * @param  Snapshot         $snapshot
     * @throws RuntimeException when the uopz_delete() function is not available
     * @see    https://github.com/krakjoe/uopz
     */
    public function restoreFunctions(Snapshot $snapshot)
    {
        if (!function_exists('uopz_delete')) {
            throw new RuntimeException('The uopz_delete() function is required for this operation');
        }

        $functions = get_defined_functions();

        foreach (array_diff($functions['user'], $snapshot->functions()) as $function) {
            uopz_delete($function);
        }
    }

    /**
     * Restores all global and super-global variables from a snapshot.
     *
     * @param Snapshot $snapshot
     */
    public function restoreGlobalVariables(Snapshot $snapshot)
    {
        $superGlobalArrays = $snapshot->superGlobalArrays();

        foreach ($superGlobalArrays as $superGlobalArray) {
            $this->restoreSuperGlobalArray($snapshot, $superGlobalArray);
        }

        $globalVariables = $snapshot->globalVariables();

        foreach (array_keys($GLOBALS) as $key) {
            if ($key != 'GLOBALS' &&
                !in_array($key, $superGlobalArrays) &&
                !$snapshot->blacklist()->isGlobalVariableBlacklisted($key)) {
                if (isset($globalVariables[$key])) {
                    $GLOBALS[$key] = $globalVariables[$key];
                } else {
                    unset($GLOBALS[$key]);
                }
            }
        }
    }

    /**
     * Restores all static attributes in user-defined classes from this snapshot.
     *
     * @param Snapshot $snapshot
     */
    public function restoreStaticAttributes(Snapshot $snapshot)
    {
        $current    = new Snapshot($snapshot->blacklist(), false, false, false, false, true, false, false, false, false);
        $newClasses = array_diff($current->classes(), $snapshot->classes());
        unset($current);

        foreach ($snapshot->staticAttributes() as $className => $staticAttributes) {
            foreach ($staticAttributes as $name => $value) {
                $reflector = new ReflectionProperty($className, $name);
                $reflector->setAccessible(true);
                $reflector->setValue($value);
            }
        }

        foreach ($newClasses as $className) {
            $class    = new \ReflectionClass($className);
            $defaults = $class->getDefaultProperties();

            foreach ($class->getProperties() as $attribute) {
                if (!$attribute->isStatic()) {
                    continue;
                }

                $name = $attribute->getName();

                if ($snapshot->blacklist()->isStaticAttributeBlacklisted($className, $name)) {
                    continue;
                }

                if (!isset($defaults[$name])) {
                    continue;
                }

                $attribute->setAccessible(true);
                $attribute->setValue($defaults[$name]);
            }
        }
    }

    /**
     * Restores a super-global variable array from this snapshot.
     *
     * @param Snapshot $snapshot
     * @param $superGlobalArray
     */
    private function restoreSuperGlobalArray(Snapshot $snapshot, $superGlobalArray)
    {
        $superGlobalVariables = $snapshot->superGlobalVariables();

        if (isset($GLOBALS[$superGlobalArray]) &&
            is_array($GLOBALS[$superGlobalArray]) &&
            isset($superGlobalVariables[$superGlobalArray])) {
            $keys = array_keys(
                array_merge(
                    $GLOBALS[$superGlobalArray],
                    $superGlobalVariables[$superGlobalArray]
                )
            );

            foreach ($keys as $key) {
                if (isset($superGlobalVariables[$superGlobalArray][$key])) {
                    $GLOBALS[$superGlobalArray][$key] = $superGlobalVariables[$superGlobalArray][$key];
                } else {
                    unset($GLOBALS[$superGlobalArray][$key]);
                }
            }
        }
    }
}
<?php
/*
 * This file is part of the GlobalState package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\GlobalState;

/**
 */
class RuntimeException extends \RuntimeException implements Exception
{
}
<?php
/*
 * This file is part of the GlobalState package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\GlobalState;

use ReflectionClass;
use Serializable;

/**
 * A snapshot of global state.
 */
class Snapshot
{
    /**
     * @var Blacklist
     */
    private $blacklist;

    /**
     * @var array
     */
    private $globalVariables = array();

    /**
     * @var array
     */
    private $superGlobalArrays = array();

    /**
     * @var array
     */
    private $superGlobalVariables = array();

    /**
     * @var array
     */
    private $staticAttributes = array();

    /**
     * @var array
     */
    private $iniSettings = array();

    /**
     * @var array
     */
    private $includedFiles = array();

    /**
     * @var array
     */
    private $constants = array();

    /**
     * @var array
     */
    private $functions = array();

    /**
     * @var array
     */
    private $interfaces = array();

    /**
     * @var array
     */
    private $classes = array();

    /**
     * @var array
     */
    private $traits = array();

    /**
     * Creates a snapshot of the current global state.
     *
     * @param Blacklist $blacklist
     * @param bool      $includeGlobalVariables
     * @param bool      $includeStaticAttributes
     * @param bool      $includeConstants
     * @param bool      $includeFunctions
     * @param bool      $includeClasses
     * @param bool      $includeInterfaces
     * @param bool      $includeTraits
     * @param bool      $includeIniSettings
     * @param bool      $includeIncludedFiles
     */
    public function __construct(Blacklist $blacklist = null, $includeGlobalVariables = true, $includeStaticAttributes = true, $includeConstants = true, $includeFunctions = true, $includeClasses = true, $includeInterfaces = true, $includeTraits = true, $includeIniSettings = true, $includeIncludedFiles = true)
    {
        if ($blacklist === null) {
            $blacklist = new Blacklist;
        }

        $this->blacklist = $blacklist;

        if ($includeConstants) {
            $this->snapshotConstants();
        }

        if ($includeFunctions) {
            $this->snapshotFunctions();
        }

        if ($includeClasses || $includeStaticAttributes) {
            $this->snapshotClasses();
        }

        if ($includeInterfaces) {
            $this->snapshotInterfaces();
        }

        if ($includeGlobalVariables) {
            $this->setupSuperGlobalArrays();
            $this->snapshotGlobals();
        }

        if ($includeStaticAttributes) {
            $this->snapshotStaticAttributes();
        }

        if ($includeIniSettings) {
            $this->iniSettings = ini_get_all(null, false);
        }

        if ($includeIncludedFiles) {
            $this->includedFiles = get_included_files();
        }

        if (function_exists('get_declared_traits')) {
            $this->traits = get_declared_traits();
        }
    }

    /**
     * @return Blacklist
     */
    public function blacklist()
    {
        return $this->blacklist;
    }

    /**
     * @return array
     */
    public function globalVariables()
    {
        return $this->globalVariables;
    }

    /**
     * @return array
     */
    public function superGlobalVariables()
    {
        return $this->superGlobalVariables;
    }

    /**
     * Returns a list of all super-global variable arrays.
     *
     * @return array
     */
    public function superGlobalArrays()
    {
        return $this->superGlobalArrays;
    }

    /**
     * @return array
     */
    public function staticAttributes()
    {
        return $this->staticAttributes;
    }

    /**
     * @return array
     */
    public function iniSettings()
    {
        return $this->iniSettings;
    }

    /**
     * @return array
     */
    public function includedFiles()
    {
        return $this->includedFiles;
    }

    /**
     * @return array
     */
    public function constants()
    {
        return $this->constants;
    }

    /**
     * @return array
     */
    public function functions()
    {
        return $this->functions;
    }

    /**
     * @return array
     */
    public function interfaces()
    {
        return $this->interfaces;
    }

    /**
     * @return array
     */
    public function classes()
    {
        return $this->classes;
    }

    /**
     * @return array
     */
    public function traits()
    {
        return $this->traits;
    }

    /**
     * Creates a snapshot user-defined constants.
     */
    private function snapshotConstants()
    {
        $constants = get_defined_constants(true);

        if (isset($constants['user'])) {
            $this->constants = $constants['user'];
        }
    }

    /**
     * Creates a snapshot user-defined functions.
     */
    private function snapshotFunctions()
    {
        $functions = get_defined_functions();

        $this->functions = $functions['user'];
    }

    /**
     * Creates a snapshot user-defined classes.
     */
    private function snapshotClasses()
    {
        foreach (array_reverse(get_declared_classes()) as $className) {
            $class = new ReflectionClass($className);

            if (!$class->isUserDefined()) {
                break;
            }

            $this->classes[] = $className;
        }

        $this->classes = array_reverse($this->classes);
    }

    /**
     * Creates a snapshot user-defined interfaces.
     */
    private function snapshotInterfaces()
    {
        foreach (array_reverse(get_declared_interfaces()) as $interfaceName) {
            $class = new ReflectionClass($interfaceName);

            if (!$class->isUserDefined()) {
                break;
            }

            $this->interfaces[] = $interfaceName;
        }

        $this->interfaces = array_reverse($this->interfaces);
    }

    /**
     * Creates a snapshot of all global and super-global variables.
     */
    private function snapshotGlobals()
    {
        $superGlobalArrays = $this->superGlobalArrays();

        foreach ($superGlobalArrays as $superGlobalArray) {
            $this->snapshotSuperGlobalArray($superGlobalArray);
        }

        foreach (array_keys($GLOBALS) as $key) {
            if ($key != 'GLOBALS' &&
                !in_array($key, $superGlobalArrays) &&
                $this->canBeSerialized($GLOBALS[$key]) &&
                !$this->blacklist->isGlobalVariableBlacklisted($key)) {
                $this->globalVariables[$key] = unserialize(serialize($GLOBALS[$key]));
            }
        }
    }

    /**
     * Creates a snapshot a super-global variable array.
     *
     * @param $superGlobalArray
     */
    private function snapshotSuperGlobalArray($superGlobalArray)
    {
        $this->superGlobalVariables[$superGlobalArray] = array();

        if (isset($GLOBALS[$superGlobalArray]) && is_array($GLOBALS[$superGlobalArray])) {
            foreach ($GLOBALS[$superGlobalArray] as $key => $value) {
                $this->superGlobalVariables[$superGlobalArray][$key] = unserialize(serialize($value));
            }
        }
    }

    /**
     * Creates a snapshot of all static attributes in user-defined classes.
     */
    private function snapshotStaticAttributes()
    {
        foreach ($this->classes as $className) {
            $class    = new ReflectionClass($className);
            $snapshot = array();

            foreach ($class->getProperties() as $attribute) {
                if ($attribute->isStatic()) {
                    $name = $attribute->getName();

                    if ($this->blacklist->isStaticAttributeBlacklisted($className, $name)) {
                        continue;
                    }

                    $attribute->setAccessible(true);
                    $value = $attribute->getValue();

                    if ($this->canBeSerialized($value)) {
                        $snapshot[$name] = unserialize(serialize($value));
                    }
                }
            }

            if (!empty($snapshot)) {
                $this->staticAttributes[$className] = $snapshot;
            }
        }
    }

    /**
     * Returns a list of all super-global variable arrays.
     *
     * @return array
     */
    private function setupSuperGlobalArrays()
    {
        $this->superGlobalArrays = array(
            '_ENV',
            '_POST',
            '_GET',
            '_COOKIE',
            '_SERVER',
            '_FILES',
            '_REQUEST'
        );

        if (ini_get('register_long_arrays') == '1') {
            $this->superGlobalArrays = array_merge(
                $this->superGlobalArrays,
                array(
                    'HTTP_ENV_VARS',
                    'HTTP_POST_VARS',
                    'HTTP_GET_VARS',
                    'HTTP_COOKIE_VARS',
                    'HTTP_SERVER_VARS',
                    'HTTP_POST_FILES'
                )
            );
        }
    }

    /**
     * @param  mixed $variable
     * @return bool
     * @todo   Implement this properly
     */
    private function canBeSerialized($variable)
    {
        if (!is_object($variable)) {
            return !is_resource($variable);
        }

        if ($variable instanceof \stdClass) {
            return true;
        }

        $class = new ReflectionClass($variable);

        do {
            if ($class->isInternal()) {
                return $variable instanceof Serializable;
            }
        } while ($class = $class->getParentClass());

        return true;
    }
}
<?php
/*
 * This file is part of Object Enumerator.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\ObjectEnumerator;

use SebastianBergmann\RecursionContext\Context;

/**
 * Traverses array structures and object graphs
 * to enumerate all referenced objects.
 */
class Enumerator
{
    /**
     * Returns an array of all objects referenced either
     * directly or indirectly by a variable.
     *
     * @param array|object $variable
     *
     * @return object[]
     */
    public function enumerate($variable)
    {
        if (!is_array($variable) && !is_object($variable)) {
            throw new InvalidArgumentException;
        }

        if (isset(func_get_args()[1])) {
            if (!func_get_args()[1] instanceof Context) {
                throw new InvalidArgumentException;
            }

            $processed = func_get_args()[1];
        } else {
            $processed = new Context;
        }

        $objects = [];

        if ($processed->contains($variable)) {
            return $objects;
        }

        $array = $variable;
        $processed->add($variable);

        if (is_array($variable)) {
            foreach ($array as $element) {
                if (!is_array($element) && !is_object($element)) {
                    continue;
                }

                $objects = array_merge(
                    $objects,
                    $this->enumerate($element, $processed)
                );
            }
        } else {
            $objects[] = $variable;
            $reflector = new \ReflectionObject($variable);

            foreach ($reflector->getProperties() as $attribute) {
                $attribute->setAccessible(true);

                try {
                    $value = $attribute->getValue($variable);
                } catch (\Throwable $e) {
                    continue;
                } catch (\Exception $e) {
                    continue;
                }

                if (!is_array($value) && !is_object($value)) {
                    continue;
                }

                $objects = array_merge(
                    $objects,
                    $this->enumerate($value, $processed)
                );
            }
        }

        return $objects;
    }
}
<?php
/*
 * This file is part of Object Enumerator.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\ObjectEnumerator;

interface Exception
{
}
<?php
/*
 * This file is part of Object Enumerator.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\ObjectEnumerator;

class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}
<?php
/*
 * This file is part of the Recursion Context package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\RecursionContext;

/**
 * A context containing previously processed arrays and objects
 * when recursively processing a value.
 */
final class Context
{
    /**
     * @var array[]
     */
    private $arrays;

    /**
     * @var \SplObjectStorage
     */
    private $objects;

    /**
     * Initialises the context
     */
    public function __construct()
    {
        $this->arrays  = array();
        $this->objects = new \SplObjectStorage;
    }

    /**
     * Adds a value to the context.
     *
     * @param array|object $value The value to add.
     *
     * @return int|string The ID of the stored value, either as a string or integer.
     *
     * @throws InvalidArgumentException Thrown if $value is not an array or object
     */
    public function add(&$value)
    {
        if (is_array($value)) {
            return $this->addArray($value);
        } elseif (is_object($value)) {
            return $this->addObject($value);
        }

        throw new InvalidArgumentException(
            'Only arrays and objects are supported'
        );
    }

    /**
     * Checks if the given value exists within the context.
     *
     * @param array|object $value The value to check.
     *
     * @return int|string|false The string or integer ID of the stored value if it has already been seen, or false if the value is not stored.
     *
     * @throws InvalidArgumentException Thrown if $value is not an array or object
     */
    public function contains(&$value)
    {
        if (is_array($value)) {
            return $this->containsArray($value);
        } elseif (is_object($value)) {
            return $this->containsObject($value);
        }

        throw new InvalidArgumentException(
            'Only arrays and objects are supported'
        );
    }

    /**
     * @param array $array
     *
     * @return bool|int
     */
    private function addArray(array &$array)
    {
        $key = $this->containsArray($array);

        if ($key !== false) {
            return $key;
        }

        $key            = count($this->arrays);
        $this->arrays[] = &$array;

        if (!isset($array[PHP_INT_MAX]) && !isset($array[PHP_INT_MAX - 1])) {
            $array[] = $key;
            $array[] = $this->objects;
        } else { /* cover the improbable case too */
            do {
                $key = random_int(PHP_INT_MIN, PHP_INT_MAX);
            } while (isset($array[$key]));

            $array[$key] = $key;

            do {
                $key = random_int(PHP_INT_MIN, PHP_INT_MAX);
            } while (isset($array[$key]));

            $array[$key] = $this->objects;
        }

        return $key;
    }

    /**
     * @param object $object
     *
     * @return string
     */
    private function addObject($object)
    {
        if (!$this->objects->contains($object)) {
            $this->objects->attach($object);
        }

        return spl_object_hash($object);
    }

    /**
     * @param array $array
     *
     * @return int|false
     */
    private function containsArray(array &$array)
    {
        $end = array_slice($array, -2);

        return isset($end[1]) && $end[1] === $this->objects ? $end[0] : false;
    }

    /**
     * @param object $value
     *
     * @return string|false
     */
    private function containsObject($value)
    {
        if ($this->objects->contains($value)) {
            return spl_object_hash($value);
        }

        return false;
    }

    public function __destruct()
    {
        foreach ($this->arrays as &$array) {
            if (is_array($array)) {
                array_pop($array);
                array_pop($array);
            }
        }
    }
}
<?php
/*
 * This file is part of the Recursion Context package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\RecursionContext;

/**
 */
interface Exception
{
}
<?php
/*
 * This file is part of the Recursion Context package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\RecursionContext;

/**
 */
final class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}
#!/usr/bin/env php
<?php
/*
 * This file is part of resource-operations.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

$functions         = require __DIR__ . '/arginfo.php';
$resourceFunctions = [];

foreach ($functions as $function => $arguments) {
    foreach ($arguments as $argument) {
        if ($argument == 'resource') {
            $resourceFunctions[] = $function;
        }
    }
}

$resourceFunctions = array_unique($resourceFunctions);
sort($resourceFunctions);

$buffer = <<<EOT
<?php
/*
 * This file is part of resource-operations.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\ResourceOperations;

class ResourceOperations
{
    /**
     * @return string[]
     */
    public static function getFunctions()
    {
        return [

EOT;

foreach ($resourceFunctions as $function) {
    $buffer .= sprintf("            '%s',\n", $function);
}

$buffer .= <<< EOT
        ];
    }
}

EOT;

file_put_contents(__DIR__ . '/../src/ResourceOperations.php', $buffer);

<?php
/*
 * This file is part of resource-operations.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann\ResourceOperations;

class ResourceOperations
{
    /**
     * @return string[]
     */
    public static function getFunctions()
    {
        return [
            'Directory::close',
            'Directory::read',
            'Directory::rewind',
            'HttpResponse::getRequestBodyStream',
            'HttpResponse::getStream',
            'MongoGridFSCursor::__construct',
            'MongoGridFSFile::getResource',
            'MysqlndUhConnection::stmtInit',
            'MysqlndUhConnection::storeResult',
            'MysqlndUhConnection::useResult',
            'PDF_new',
            'PDO::pgsqlLOBOpen',
            'RarEntry::getStream',
            'SQLite3::openBlob',
            'XMLWriter::openMemory',
            'XMLWriter::openURI',
            'ZipArchive::getStream',
            'bbcode_create',
            'bzopen',
            'crack_opendict',
            'cubrid_connect',
            'cubrid_connect_with_url',
            'cubrid_get_query_timeout',
            'cubrid_lob2_bind',
            'cubrid_lob2_close',
            'cubrid_lob2_export',
            'cubrid_lob2_import',
            'cubrid_lob2_new',
            'cubrid_lob2_read',
            'cubrid_lob2_seek',
            'cubrid_lob2_seek64',
            'cubrid_lob2_size',
            'cubrid_lob2_size64',
            'cubrid_lob2_tell',
            'cubrid_lob2_tell64',
            'cubrid_lob2_write',
            'cubrid_pconnect',
            'cubrid_pconnect_with_url',
            'cubrid_prepare',
            'cubrid_query',
            'cubrid_set_query_timeout',
            'cubrid_unbuffered_query',
            'curl_copy_handle',
            'curl_getinfo',
            'curl_init',
            'curl_multi_add_handle',
            'curl_multi_close',
            'curl_multi_exec',
            'curl_multi_getcontent',
            'curl_multi_info_read',
            'curl_multi_init',
            'curl_multi_remove_handle',
            'curl_multi_select',
            'curl_multi_setopt',
            'curl_pause',
            'curl_reset',
            'curl_setopt',
            'curl_setopt_array',
            'curl_share_close',
            'curl_share_init',
            'curl_share_setopt',
            'curl_unescape',
            'cyrus_connect',
            'db2_column_privileges',
            'db2_columns',
            'db2_connect',
            'db2_exec',
            'db2_foreign_keys',
            'db2_next_result',
            'db2_pconnect',
            'db2_prepare',
            'db2_primary_keys',
            'db2_procedure_columns',
            'db2_procedures',
            'db2_special_columns',
            'db2_statistics',
            'db2_table_privileges',
            'db2_tables',
            'dba_fetch',
            'dba_fetch 1',
            'dba_open',
            'dba_popen',
            'dbplus_aql',
            'dbplus_open',
            'dbplus_rcreate',
            'dbplus_ropen',
            'dbplus_rquery',
            'dbplus_sql',
            'deflate_init',
            'dio_open',
            'eio_busy',
            'eio_cancel',
            'eio_chmod',
            'eio_chown',
            'eio_close',
            'eio_custom',
            'eio_dup2',
            'eio_fallocate',
            'eio_fchmod',
            'eio_fchown',
            'eio_fdatasync',
            'eio_fstat',
            'eio_fstatvfs',
            'eio_fsync',
            'eio_ftruncate',
            'eio_futime',
            'eio_get_last_error',
            'eio_grp',
            'eio_grp_add',
            'eio_grp_cancel',
            'eio_grp_limit',
            'eio_link',
            'eio_lstat',
            'eio_mkdir',
            'eio_mknod',
            'eio_nop',
            'eio_open',
            'eio_read',
            'eio_readahead',
            'eio_readdir',
            'eio_readlink',
            'eio_realpath',
            'eio_rename',
            'eio_rmdir',
            'eio_seek',
            'eio_sendfile',
            'eio_stat',
            'eio_statvfs',
            'eio_symlink',
            'eio_sync',
            'eio_sync_file_range',
            'eio_syncfs',
            'eio_truncate',
            'eio_unlink',
            'eio_utime',
            'eio_write',
            'enchant_broker_free_dict',
            'enchant_broker_init',
            'enchant_broker_request_dict',
            'enchant_broker_request_pwl_dict',
            'event_base_new',
            'event_base_reinit',
            'event_buffer_new',
            'event_new',
            'event_priority_set',
            'event_timer_set',
            'expect_popen',
            'fam_monitor_collection',
            'fam_monitor_directory',
            'fam_monitor_file',
            'fam_open',
            'fann_cascadetrain_on_data',
            'fann_cascadetrain_on_file',
            'fann_clear_scaling_params',
            'fann_copy',
            'fann_create_from_file',
            'fann_create_shortcut_array',
            'fann_create_standard',
            'fann_create_standard_array',
            'fann_create_train',
            'fann_create_train_from_callback',
            'fann_descale_input',
            'fann_descale_output',
            'fann_descale_train',
            'fann_destroy',
            'fann_destroy_train',
            'fann_duplicate_train_data',
            'fann_get_MSE',
            'fann_get_activation_function',
            'fann_get_activation_steepness',
            'fann_get_bias_array',
            'fann_get_bit_fail',
            'fann_get_bit_fail_limit',
            'fann_get_cascade_activation_functions',
            'fann_get_cascade_activation_functions_count',
            'fann_get_cascade_activation_steepnesses',
            'fann_get_cascade_activation_steepnesses_count',
            'fann_get_cascade_candidate_change_fraction',
            'fann_get_cascade_candidate_limit',
            'fann_get_cascade_candidate_stagnation_epochs',
            'fann_get_cascade_max_cand_epochs',
            'fann_get_cascade_max_out_epochs',
            'fann_get_cascade_min_cand_epochs',
            'fann_get_cascade_min_out_epochs',
            'fann_get_cascade_num_candidate_groups',
            'fann_get_cascade_num_candidates',
            'fann_get_cascade_output_change_fraction',
            'fann_get_cascade_output_stagnation_epochs',
            'fann_get_cascade_weight_multiplier',
            'fann_get_connection_array',
            'fann_get_connection_rate',
            'fann_get_errno',
            'fann_get_errstr',
            'fann_get_layer_array',
            'fann_get_learning_momentum',
            'fann_get_learning_rate',
            'fann_get_network_type',
            'fann_get_num_input',
            'fann_get_num_layers',
            'fann_get_num_output',
            'fann_get_quickprop_decay',
            'fann_get_quickprop_mu',
            'fann_get_rprop_decrease_factor',
            'fann_get_rprop_delta_max',
            'fann_get_rprop_delta_min',
            'fann_get_rprop_delta_zero',
            'fann_get_rprop_increase_factor',
            'fann_get_sarprop_step_error_shift',
            'fann_get_sarprop_step_error_threshold_factor',
            'fann_get_sarprop_temperature',
            'fann_get_sarprop_weight_decay_shift',
            'fann_get_total_connections',
            'fann_get_total_neurons',
            'fann_get_train_error_function',
            'fann_get_train_stop_function',
            'fann_get_training_algorithm',
            'fann_init_weights',
            'fann_length_train_data',
            'fann_merge_train_data',
            'fann_num_input_train_data',
            'fann_num_output_train_data',
            'fann_randomize_weights',
            'fann_read_train_from_file',
            'fann_reset_errno',
            'fann_reset_errstr',
            'fann_run',
            'fann_save',
            'fann_save_train',
            'fann_scale_input',
            'fann_scale_input_train_data',
            'fann_scale_output',
            'fann_scale_output_train_data',
            'fann_scale_train',
            'fann_scale_train_data',
            'fann_set_activation_function',
            'fann_set_activation_function_hidden',
            'fann_set_activation_function_layer',
            'fann_set_activation_function_output',
            'fann_set_activation_steepness',
            'fann_set_activation_steepness_hidden',
            'fann_set_activation_steepness_layer',
            'fann_set_activation_steepness_output',
            'fann_set_bit_fail_limit',
            'fann_set_callback',
            'fann_set_cascade_activation_functions',
            'fann_set_cascade_activation_steepnesses',
            'fann_set_cascade_candidate_change_fraction',
            'fann_set_cascade_candidate_limit',
            'fann_set_cascade_candidate_stagnation_epochs',
            'fann_set_cascade_max_cand_epochs',
            'fann_set_cascade_max_out_epochs',
            'fann_set_cascade_min_cand_epochs',
            'fann_set_cascade_min_out_epochs',
            'fann_set_cascade_num_candidate_groups',
            'fann_set_cascade_output_change_fraction',
            'fann_set_cascade_output_stagnation_epochs',
            'fann_set_cascade_weight_multiplier',
            'fann_set_error_log',
            'fann_set_input_scaling_params',
            'fann_set_learning_momentum',
            'fann_set_learning_rate',
            'fann_set_output_scaling_params',
            'fann_set_quickprop_decay',
            'fann_set_quickprop_mu',
            'fann_set_rprop_decrease_factor',
            'fann_set_rprop_delta_max',
            'fann_set_rprop_delta_min',
            'fann_set_rprop_delta_zero',
            'fann_set_rprop_increase_factor',
            'fann_set_sarprop_step_error_shift',
            'fann_set_sarprop_step_error_threshold_factor',
            'fann_set_sarprop_temperature',
            'fann_set_sarprop_weight_decay_shift',
            'fann_set_scaling_params',
            'fann_set_train_error_function',
            'fann_set_train_stop_function',
            'fann_set_training_algorithm',
            'fann_set_weight',
            'fann_set_weight_array',
            'fann_shuffle_train_data',
            'fann_subset_train_data',
            'fann_test',
            'fann_test_data',
            'fann_train',
            'fann_train_epoch',
            'fann_train_on_data',
            'fann_train_on_file',
            'fbsql_connect',
            'fbsql_db_query',
            'fbsql_list_dbs',
            'fbsql_list_fields',
            'fbsql_list_tables',
            'fbsql_pconnect',
            'fbsql_query',
            'fdf_create',
            'fdf_open',
            'fdf_open_string',
            'finfo::buffer',
            'finfo_buffer',
            'finfo_close',
            'finfo_file',
            'finfo_open',
            'finfo_set_flags',
            'fopen',
            'fsockopen',
            'ftp_alloc',
            'ftp_cdup',
            'ftp_chdir',
            'ftp_chmod',
            'ftp_close',
            'ftp_connect',
            'ftp_delete',
            'ftp_exec',
            'ftp_fget',
            'ftp_fput',
            'ftp_get',
            'ftp_get_option',
            'ftp_login',
            'ftp_mdtm',
            'ftp_mkdir',
            'ftp_nb_continue',
            'ftp_nb_fget',
            'ftp_nb_fput',
            'ftp_nb_get',
            'ftp_nb_put',
            'ftp_nlist',
            'ftp_pasv',
            'ftp_put',
            'ftp_pwd',
            'ftp_raw',
            'ftp_rawlist',
            'ftp_rename',
            'ftp_rmdir',
            'ftp_set_option',
            'ftp_site',
            'ftp_size',
            'ftp_ssl_connect',
            'ftp_systype',
            'gnupg_init',
            'gupnp_context_new',
            'gupnp_control_point_new',
            'gupnp_device_info_get_service',
            'gupnp_root_device_new',
            'gzopen',
            'hash_copy',
            'hash_final',
            'hash_init',
            'hash_update',
            'hash_update_file',
            'hash_update_stream',
            'http_get_request_body_stream',
            'ibase_blob_create',
            'ibase_blob_open',
            'ibase_blob_open 1',
            'ibase_connect',
            'ibase_pconnect',
            'ibase_prepare',
            'ibase_service_attach',
            'ibase_set_event_handler',
            'ibase_set_event_handler 1',
            'ibase_trans',
            'ifx_connect',
            'ifx_pconnect',
            'ifx_prepare',
            'ifx_query',
            'imageaffine',
            'imageconvolution',
            'imagecreate',
            'imagecreatefromgd',
            'imagecreatefromgd2',
            'imagecreatefromgd2part',
            'imagecreatefromgif',
            'imagecreatefromjpeg',
            'imagecreatefrompng',
            'imagecreatefromstring',
            'imagecreatefromwbmp',
            'imagecreatefromwebp',
            'imagecreatefromxbm',
            'imagecreatefromxpm',
            'imagecreatetruecolor',
            'imagegrabscreen',
            'imagegrabwindow',
            'imagepalettetotruecolor',
            'imagepsloadfont',
            'imagerotate',
            'imagescale',
            'imap_open',
            'inflate_init',
            'ingres_connect',
            'ingres_pconnect',
            'inotify_init',
            'kadm5_init_with_password',
            'ldap_connect',
            'ldap_first_entry',
            'ldap_first_reference',
            'ldap_list',
            'ldap_next_entry',
            'ldap_next_reference',
            'ldap_read',
            'ldap_search',
            'm_initconn',
            'mailparse_msg_create',
            'mailparse_msg_get_part',
            'mailparse_msg_parse_file',
            'maxdb::use_result',
            'maxdb_connect',
            'maxdb_embedded_connect',
            'maxdb_init',
            'maxdb_stmt::result_metadata',
            'maxdb_stmt_result_metadata',
            'maxdb_use_result',
            'mcrypt_module_open',
            'msg_get_queue',
            'msql_connect',
            'msql_db_query',
            'msql_list_dbs',
            'msql_list_fields',
            'msql_list_tables',
            'msql_pconnect',
            'msql_query',
            'mssql_connect',
            'mssql_init',
            'mssql_pconnect',
            'mysql_connect',
            'mysql_db_query',
            'mysql_list_dbs',
            'mysql_list_fields',
            'mysql_list_processes',
            'mysql_list_tables',
            'mysql_pconnect',
            'mysql_query',
            'mysql_unbuffered_query',
            'mysqlnd_uh_convert_to_mysqlnd',
            'ncurses_new_panel',
            'ncurses_newpad',
            'ncurses_newwin',
            'ncurses_panel_above',
            'ncurses_panel_below',
            'ncurses_panel_window',
            'newt_button',
            'newt_button_bar',
            'newt_checkbox',
            'newt_checkbox_tree',
            'newt_checkbox_tree_multi',
            'newt_compact_button',
            'newt_create_grid',
            'newt_entry',
            'newt_form',
            'newt_form_get_current',
            'newt_grid_basic_window',
            'newt_grid_h_close_stacked',
            'newt_grid_h_stacked',
            'newt_grid_simple_window',
            'newt_grid_v_close_stacked',
            'newt_grid_v_stacked',
            'newt_label',
            'newt_listbox',
            'newt_listitem',
            'newt_radio_get_current',
            'newt_radiobutton',
            'newt_run_form',
            'newt_scale',
            'newt_textbox',
            'newt_textbox_reflowed',
            'newt_vertical_scrollbar',
            'oci_connect',
            'oci_get_implicit_resultset',
            'oci_new_connect',
            'oci_new_cursor',
            'oci_parse',
            'oci_pconnect',
            'odbc_columnprivileges',
            'odbc_columns',
            'odbc_connect',
            'odbc_exec',
            'odbc_foreignkeys',
            'odbc_gettypeinfo',
            'odbc_pconnect',
            'odbc_prepare',
            'odbc_primarykeys',
            'odbc_procedurecolumns',
            'odbc_procedures',
            'odbc_specialcolumns',
            'odbc_statistics',
            'odbc_tableprivileges',
            'odbc_tables',
            'openal_buffer_create',
            'openal_context_create',
            'openal_device_open',
            'openal_source_create',
            'openal_stream',
            'openssl_csr_new',
            'openssl_csr_sign',
            'openssl_pkey_get_private',
            'openssl_pkey_get_public',
            'openssl_pkey_new',
            'openssl_x509_read',
            'pfsockopen',
            'pg_cancel_query',
            'pg_client_encoding',
            'pg_close',
            'pg_connect',
            'pg_connect_poll',
            'pg_connection_busy',
            'pg_connection_reset',
            'pg_connection_status',
            'pg_consume_input',
            'pg_copy_from',
            'pg_copy_to',
            'pg_dbname',
            'pg_end_copy',
            'pg_escape_bytea',
            'pg_escape_identifier',
            'pg_escape_identifier 1',
            'pg_escape_literal',
            'pg_escape_string',
            'pg_execute',
            'pg_execute 1',
            'pg_flush',
            'pg_free_result',
            'pg_get_notify',
            'pg_get_pid',
            'pg_get_result',
            'pg_host',
            'pg_last_error',
            'pg_last_notice',
            'pg_lo_create',
            'pg_lo_export',
            'pg_lo_import',
            'pg_lo_open',
            'pg_lo_unlink',
            'pg_options',
            'pg_parameter_status',
            'pg_pconnect',
            'pg_ping',
            'pg_port',
            'pg_prepare',
            'pg_prepare 1',
            'pg_put_line',
            'pg_query',
            'pg_query 1',
            'pg_query_params',
            'pg_query_params 1',
            'pg_send_execute',
            'pg_send_prepare',
            'pg_send_query',
            'pg_send_query_params',
            'pg_set_client_encoding',
            'pg_set_client_encoding 1',
            'pg_set_error_verbosity',
            'pg_socket',
            'pg_trace',
            'pg_transaction_status',
            'pg_tty',
            'pg_untrace',
            'pg_version',
            'php_user_filter::filter',
            'popen',
            'proc_open',
            'ps_new',
            'px_new',
            'radius_acct_open',
            'radius_auth_open',
            'radius_salt_encrypt_attr',
            'rpm_open',
            'sem_get',
            'shm_attach',
            'socket_accept',
            'socket_create',
            'socket_create_listen',
            'socket_recvmsg',
            'socket_sendmsg',
            'sqlite_open',
            'sqlite_popen',
            'sqlsrv_begin_transaction',
            'sqlsrv_cancel',
            'sqlsrv_client_info',
            'sqlsrv_close',
            'sqlsrv_commit',
            'sqlsrv_connect',
            'sqlsrv_execute',
            'sqlsrv_fetch',
            'sqlsrv_fetch_array',
            'sqlsrv_fetch_object',
            'sqlsrv_field_metadata',
            'sqlsrv_free_stmt',
            'sqlsrv_get_field',
            'sqlsrv_has_rows',
            'sqlsrv_next_result',
            'sqlsrv_num_fields',
            'sqlsrv_num_rows',
            'sqlsrv_prepare',
            'sqlsrv_query',
            'sqlsrv_rollback',
            'sqlsrv_rows_affected',
            'sqlsrv_send_stream_data',
            'sqlsrv_server_info',
            'ssh2_auth_agent',
            'ssh2_connect',
            'ssh2_exec',
            'ssh2_fetch_stream',
            'ssh2_publickey_init',
            'ssh2_sftp',
            'ssh2_sftp_chmod',
            'ssh2_shell',
            'ssh2_tunnel',
            'stomp_connect',
            'streamWrapper::stream_cast',
            'stream_bucket_new',
            'stream_context_create',
            'stream_context_get_default',
            'stream_context_set_default',
            'stream_filter_append',
            'stream_filter_prepend',
            'stream_socket_accept',
            'stream_socket_client',
            'stream_socket_server',
            'svn_fs_apply_text',
            'svn_fs_begin_txn2',
            'svn_fs_file_contents',
            'svn_fs_revision_root',
            'svn_fs_txn_root',
            'svn_repos_create',
            'svn_repos_fs',
            'svn_repos_fs_begin_txn_for_commit',
            'svn_repos_open',
            'sybase_connect',
            'sybase_pconnect',
            'sybase_unbuffered_query',
            'tmpfile',
            'udm_alloc_agent',
            'udm_alloc_agent_array',
            'udm_find',
            'unlink',
            'w32api_init_dtype',
            'wddx_packet_start',
            'xml_parser_create',
            'xml_parser_create_ns',
            'xml_parser_free',
            'xml_parser_get_option',
            'xml_parser_set_option',
            'xmlrpc_server_create',
            'xmlwriter_open_memory',
            'xmlwriter_open_uri',
            'xslt_create',
            'zip_open',
            'zip_read',
        ];
    }
}
<?php
/*
 * This file is part of the Version package.
 *
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SebastianBergmann;

/**
 * @since Class available since Release 1.0.0
 */
class Version
{
    /**
     * @var string
     */
    private $path;

    /**
     * @var string
     */
    private $release;

    /**
     * @var string
     */
    private $version;

    /**
     * @param string $release
     * @param string $path
     */
    public function __construct($release, $path)
    {
        $this->release = $release;
        $this->path    = $path;
    }

    /**
     * @return string
     */
    public function getVersion()
    {
        if ($this->version === null) {
            if (count(explode('.', $this->release)) == 3) {
                $this->version = $this->release;
            } else {
                $this->version = $this->release . '-dev';
            }

            $git = $this->getGitInformation($this->path);

            if ($git) {
                if (count(explode('.', $this->release)) == 3) {
                    $this->version = $git;
                } else {
                    $git = explode('-', $git);

                    $this->version = $this->release . '-' . end($git);
                }
            }
        }

        return $this->version;
    }

    /**
     * @param string $path
     *
     * @return bool|string
     */
    private function getGitInformation($path)
    {
        if (!is_dir($path . DIRECTORY_SEPARATOR . '.git')) {
            return false;
        }

        $process = proc_open(
            'git describe --tags',
            [
                1 => ['pipe', 'w'],
                2 => ['pipe', 'w'],
            ],
            $pipes,
            $path
        );

        if (!is_resource($process)) {
            return false;
        }

        $result = trim(stream_get_contents($pipes[1]));

        fclose($pipes[1]);
        fclose($pipes[2]);

        $returnCode = proc_close($process);

        if ($returnCode !== 0) {
            return false;
        }

        return $result;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Lazily loads listeners and subscribers from the dependency injection
 * container.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Bernhard Schussek <bschussek@gmail.com>
 * @author Jordan Alliot <jordan.alliot@gmail.com>
 */
class ContainerAwareEventDispatcher extends EventDispatcher
{
    /**
     * The container from where services are loaded.
     *
     * @var ContainerInterface
     */
    private $container;

    /**
     * The service IDs of the event listeners and subscribers.
     *
     * @var array
     */
    private $listenerIds = array();

    /**
     * The services registered as listeners.
     *
     * @var array
     */
    private $listeners = array();

    /**
     * Constructor.
     *
     * @param ContainerInterface $container A ContainerInterface instance
     */
    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    /**
     * Adds a service as event listener.
     *
     * @param string $eventName Event for which the listener is added
     * @param array  $callback  The service ID of the listener service & the method
     *                          name that has to be called
     * @param int    $priority  The higher this value, the earlier an event listener
     *                          will be triggered in the chain.
     *                          Defaults to 0.
     *
     * @throws \InvalidArgumentException
     */
    public function addListenerService($eventName, $callback, $priority = 0)
    {
        if (!is_array($callback) || 2 !== count($callback)) {
            throw new \InvalidArgumentException('Expected an array("service", "method") argument');
        }

        $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority);
    }

    public function removeListener($eventName, $listener)
    {
        $this->lazyLoad($eventName);

        if (isset($this->listenerIds[$eventName])) {
            foreach ($this->listenerIds[$eventName] as $i => $args) {
                list($serviceId, $method, $priority) = $args;
                $key = $serviceId.'.'.$method;
                if (isset($this->listeners[$eventName][$key]) && $listener === array($this->listeners[$eventName][$key], $method)) {
                    unset($this->listeners[$eventName][$key]);
                    if (empty($this->listeners[$eventName])) {
                        unset($this->listeners[$eventName]);
                    }
                    unset($this->listenerIds[$eventName][$i]);
                    if (empty($this->listenerIds[$eventName])) {
                        unset($this->listenerIds[$eventName]);
                    }
                }
            }
        }

        parent::removeListener($eventName, $listener);
    }

    /**
     * {@inheritdoc}
     */
    public function hasListeners($eventName = null)
    {
        if (null === $eventName) {
            return $this->listenerIds || $this->listeners || parent::hasListeners();
        }

        if (isset($this->listenerIds[$eventName])) {
            return true;
        }

        return parent::hasListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function getListeners($eventName = null)
    {
        if (null === $eventName) {
            foreach ($this->listenerIds as $serviceEventName => $args) {
                $this->lazyLoad($serviceEventName);
            }
        } else {
            $this->lazyLoad($eventName);
        }

        return parent::getListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function getListenerPriority($eventName, $listener)
    {
        $this->lazyLoad($eventName);

        return parent::getListenerPriority($eventName, $listener);
    }

    /**
     * Adds a service as event subscriber.
     *
     * @param string $serviceId The service ID of the subscriber service
     * @param string $class     The service's class name (which must implement EventSubscriberInterface)
     */
    public function addSubscriberService($serviceId, $class)
    {
        foreach ($class::getSubscribedEvents() as $eventName => $params) {
            if (is_string($params)) {
                $this->listenerIds[$eventName][] = array($serviceId, $params, 0);
            } elseif (is_string($params[0])) {
                $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0);
            } else {
                foreach ($params as $listener) {
                    $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0);
                }
            }
        }
    }

    public function getContainer()
    {
        return $this->container;
    }

    /**
     * Lazily loads listeners for this event from the dependency injection
     * container.
     *
     * @param string $eventName The name of the event to dispatch. The name of
     *                          the event is the name of the method that is
     *                          invoked on listeners.
     */
    protected function lazyLoad($eventName)
    {
        if (isset($this->listenerIds[$eventName])) {
            foreach ($this->listenerIds[$eventName] as $args) {
                list($serviceId, $method, $priority) = $args;
                $listener = $this->container->get($serviceId);

                $key = $serviceId.'.'.$method;
                if (!isset($this->listeners[$eventName][$key])) {
                    $this->addListener($eventName, array($listener, $method), $priority);
                } elseif ($listener !== $this->listeners[$eventName][$key]) {
                    parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method));
                    $this->addListener($eventName, array($listener, $method), $priority);
                }

                $this->listeners[$eventName][$key] = $listener;
            }
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\Debug;

use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\Stopwatch\Stopwatch;
use Psr\Log\LoggerInterface;

/**
 * Collects some data about event listeners.
 *
 * This event dispatcher delegates the dispatching to another one.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class TraceableEventDispatcher implements TraceableEventDispatcherInterface
{
    protected $logger;
    protected $stopwatch;

    private $called;
    private $dispatcher;
    private $wrappedListeners;

    /**
     * Constructor.
     *
     * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
     * @param Stopwatch                $stopwatch  A Stopwatch instance
     * @param LoggerInterface          $logger     A LoggerInterface instance
     */
    public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null)
    {
        $this->dispatcher = $dispatcher;
        $this->stopwatch = $stopwatch;
        $this->logger = $logger;
        $this->called = array();
        $this->wrappedListeners = array();
    }

    /**
     * {@inheritdoc}
     */
    public function addListener($eventName, $listener, $priority = 0)
    {
        $this->dispatcher->addListener($eventName, $listener, $priority);
    }

    /**
     * {@inheritdoc}
     */
    public function addSubscriber(EventSubscriberInterface $subscriber)
    {
        $this->dispatcher->addSubscriber($subscriber);
    }

    /**
     * {@inheritdoc}
     */
    public function removeListener($eventName, $listener)
    {
        if (isset($this->wrappedListeners[$eventName])) {
            foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
                if ($wrappedListener->getWrappedListener() === $listener) {
                    $listener = $wrappedListener;
                    unset($this->wrappedListeners[$eventName][$index]);
                    break;
                }
            }
        }

        return $this->dispatcher->removeListener($eventName, $listener);
    }

    /**
     * {@inheritdoc}
     */
    public function removeSubscriber(EventSubscriberInterface $subscriber)
    {
        return $this->dispatcher->removeSubscriber($subscriber);
    }

    /**
     * {@inheritdoc}
     */
    public function getListeners($eventName = null)
    {
        return $this->dispatcher->getListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function getListenerPriority($eventName, $listener)
    {
        if (!method_exists($this->dispatcher, 'getListenerPriority')) {
            return 0;
        }

        return $this->dispatcher->getListenerPriority($eventName, $listener);
    }

    /**
     * {@inheritdoc}
     */
    public function hasListeners($eventName = null)
    {
        return $this->dispatcher->hasListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function dispatch($eventName, Event $event = null)
    {
        if (null === $event) {
            $event = new Event();
        }

        if (null !== $this->logger && $event->isPropagationStopped()) {
            $this->logger->debug(sprintf('The "%s" event is already stopped. No listeners have been called.', $eventName));
        }

        $this->preProcess($eventName);
        $this->preDispatch($eventName, $event);

        $e = $this->stopwatch->start($eventName, 'section');

        $this->dispatcher->dispatch($eventName, $event);

        if ($e->isStarted()) {
            $e->stop();
        }

        $this->postDispatch($eventName, $event);
        $this->postProcess($eventName);

        return $event;
    }

    /**
     * {@inheritdoc}
     */
    public function getCalledListeners()
    {
        $called = array();
        foreach ($this->called as $eventName => $listeners) {
            foreach ($listeners as $listener) {
                $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName);
                $called[$eventName.'.'.$info['pretty']] = $info;
            }
        }

        return $called;
    }

    /**
     * {@inheritdoc}
     */
    public function getNotCalledListeners()
    {
        try {
            $allListeners = $this->getListeners();
        } catch (\Exception $e) {
            if (null !== $this->logger) {
                $this->logger->info('An exception was thrown while getting the uncalled listeners.', array('exception' => $e));
            }

            // unable to retrieve the uncalled listeners
            return array();
        }

        $notCalled = array();
        foreach ($allListeners as $eventName => $listeners) {
            foreach ($listeners as $listener) {
                $called = false;
                if (isset($this->called[$eventName])) {
                    foreach ($this->called[$eventName] as $l) {
                        if ($l->getWrappedListener() === $listener) {
                            $called = true;

                            break;
                        }
                    }
                }

                if (!$called) {
                    $info = $this->getListenerInfo($listener, $eventName);
                    $notCalled[$eventName.'.'.$info['pretty']] = $info;
                }
            }
        }

        uasort($notCalled, array($this, 'sortListenersByPriority'));

        return $notCalled;
    }

    /**
     * Proxies all method calls to the original event dispatcher.
     *
     * @param string $method    The method name
     * @param array  $arguments The method arguments
     *
     * @return mixed
     */
    public function __call($method, $arguments)
    {
        return call_user_func_array(array($this->dispatcher, $method), $arguments);
    }

    /**
     * Called before dispatching the event.
     *
     * @param string $eventName The event name
     * @param Event  $event     The event
     */
    protected function preDispatch($eventName, Event $event)
    {
    }

    /**
     * Called after dispatching the event.
     *
     * @param string $eventName The event name
     * @param Event  $event     The event
     */
    protected function postDispatch($eventName, Event $event)
    {
    }

    private function preProcess($eventName)
    {
        foreach ($this->dispatcher->getListeners($eventName) as $listener) {
            $info = $this->getListenerInfo($listener, $eventName);
            $name = isset($info['class']) ? $info['class'] : $info['type'];
            $wrappedListener = new WrappedListener($listener, $name, $this->stopwatch, $this);
            $this->wrappedListeners[$eventName][] = $wrappedListener;
            $this->dispatcher->removeListener($eventName, $listener);
            $this->dispatcher->addListener($eventName, $wrappedListener, $info['priority']);
        }
    }

    private function postProcess($eventName)
    {
        unset($this->wrappedListeners[$eventName]);
        $skipped = false;
        foreach ($this->dispatcher->getListeners($eventName) as $listener) {
            if (!$listener instanceof WrappedListener) { // #12845: a new listener was added during dispatch.
                continue;
            }
            // Unwrap listener
            $priority = $this->getListenerPriority($eventName, $listener);
            $this->dispatcher->removeListener($eventName, $listener);
            $this->dispatcher->addListener($eventName, $listener->getWrappedListener(), $priority);

            $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName);
            if ($listener->wasCalled()) {
                if (null !== $this->logger) {
                    $this->logger->debug(sprintf('Notified event "%s" to listener "%s".', $eventName, $info['pretty']));
                }

                if (!isset($this->called[$eventName])) {
                    $this->called[$eventName] = new \SplObjectStorage();
                }

                $this->called[$eventName]->attach($listener);
            }

            if (null !== $this->logger && $skipped) {
                $this->logger->debug(sprintf('Listener "%s" was not called for event "%s".', $info['pretty'], $eventName));
            }

            if ($listener->stoppedPropagation()) {
                if (null !== $this->logger) {
                    $this->logger->debug(sprintf('Listener "%s" stopped propagation of the event "%s".', $info['pretty'], $eventName));
                }

                $skipped = true;
            }
        }
    }

    /**
     * Returns information about the listener.
     *
     * @param object $listener  The listener
     * @param string $eventName The event name
     *
     * @return array Information about the listener
     */
    private function getListenerInfo($listener, $eventName)
    {
        $info = array(
            'event' => $eventName,
            'priority' => $this->getListenerPriority($eventName, $listener),
        );

        // unwrap for correct listener info
        if ($listener instanceof WrappedListener) {
            $listener = $listener->getWrappedListener();
        }

        if ($listener instanceof \Closure) {
            $info += array(
                'type' => 'Closure',
                'pretty' => 'closure',
            );
        } elseif (is_string($listener)) {
            try {
                $r = new \ReflectionFunction($listener);
                $file = $r->getFileName();
                $line = $r->getStartLine();
            } catch (\ReflectionException $e) {
                $file = null;
                $line = null;
            }
            $info += array(
                'type' => 'Function',
                'function' => $listener,
                'file' => $file,
                'line' => $line,
                'pretty' => $listener,
            );
        } elseif (is_array($listener) || (is_object($listener) && is_callable($listener))) {
            if (!is_array($listener)) {
                $listener = array($listener, '__invoke');
            }
            $class = is_object($listener[0]) ? get_class($listener[0]) : $listener[0];
            try {
                $r = new \ReflectionMethod($class, $listener[1]);
                $file = $r->getFileName();
                $line = $r->getStartLine();
            } catch (\ReflectionException $e) {
                $file = null;
                $line = null;
            }
            $info += array(
                'type' => 'Method',
                'class' => $class,
                'method' => $listener[1],
                'file' => $file,
                'line' => $line,
                'pretty' => $class.'::'.$listener[1],
            );
        }

        return $info;
    }

    private function sortListenersByPriority($a, $b)
    {
        if (is_int($a['priority']) && !is_int($b['priority'])) {
            return 1;
        }

        if (!is_int($a['priority']) && is_int($b['priority'])) {
            return -1;
        }

        if ($a['priority'] === $b['priority']) {
            return 0;
        }

        if ($a['priority'] > $b['priority']) {
            return -1;
        }

        return 1;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\Debug;

use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface TraceableEventDispatcherInterface extends EventDispatcherInterface
{
    /**
     * Gets the called listeners.
     *
     * @return array An array of called listeners
     */
    public function getCalledListeners();

    /**
     * Gets the not called listeners.
     *
     * @return array An array of not called listeners
     */
    public function getNotCalledListeners();
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\Debug;

use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
class WrappedListener
{
    private $listener;
    private $name;
    private $called;
    private $stoppedPropagation;
    private $stopwatch;
    private $dispatcher;

    public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null)
    {
        $this->listener = $listener;
        $this->name = $name;
        $this->stopwatch = $stopwatch;
        $this->dispatcher = $dispatcher;
        $this->called = false;
        $this->stoppedPropagation = false;
    }

    public function getWrappedListener()
    {
        return $this->listener;
    }

    public function wasCalled()
    {
        return $this->called;
    }

    public function stoppedPropagation()
    {
        return $this->stoppedPropagation;
    }

    public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher)
    {
        $this->called = true;

        $e = $this->stopwatch->start($this->name, 'event_listener');

        call_user_func($this->listener, $event, $eventName, $this->dispatcher ?: $dispatcher);

        if ($e->isStarted()) {
            $e->stop();
        }

        if ($event->isPropagationStopped()) {
            $this->stoppedPropagation = true;
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;

/**
 * Compiler pass to register tagged services for an event dispatcher.
 */
class RegisterListenersPass implements CompilerPassInterface
{
    /**
     * @var string
     */
    protected $dispatcherService;

    /**
     * @var string
     */
    protected $listenerTag;

    /**
     * @var string
     */
    protected $subscriberTag;

    /**
     * Constructor.
     *
     * @param string $dispatcherService Service name of the event dispatcher in processed container
     * @param string $listenerTag       Tag name used for listener
     * @param string $subscriberTag     Tag name used for subscribers
     */
    public function __construct($dispatcherService = 'event_dispatcher', $listenerTag = 'kernel.event_listener', $subscriberTag = 'kernel.event_subscriber')
    {
        $this->dispatcherService = $dispatcherService;
        $this->listenerTag = $listenerTag;
        $this->subscriberTag = $subscriberTag;
    }

    public function process(ContainerBuilder $container)
    {
        if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) {
            return;
        }

        $definition = $container->findDefinition($this->dispatcherService);

        foreach ($container->findTaggedServiceIds($this->listenerTag) as $id => $events) {
            $def = $container->getDefinition($id);
            if (!$def->isPublic()) {
                throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event listeners are lazy-loaded.', $id));
            }

            if ($def->isAbstract()) {
                throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event listeners are lazy-loaded.', $id));
            }

            foreach ($events as $event) {
                $priority = isset($event['priority']) ? $event['priority'] : 0;

                if (!isset($event['event'])) {
                    throw new \InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag));
                }

                if (!isset($event['method'])) {
                    $event['method'] = 'on'.preg_replace_callback(array(
                        '/(?<=\b)[a-z]/i',
                        '/[^a-z0-9]/i',
                    ), function ($matches) { return strtoupper($matches[0]); }, $event['event']);
                    $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']);
                }

                $definition->addMethodCall('addListenerService', array($event['event'], array($id, $event['method']), $priority));
            }
        }

        foreach ($container->findTaggedServiceIds($this->subscriberTag) as $id => $attributes) {
            $def = $container->getDefinition($id);
            if (!$def->isPublic()) {
                throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event subscribers are lazy-loaded.', $id));
            }

            if ($def->isAbstract()) {
                throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event subscribers are lazy-loaded.', $id));
            }

            // We must assume that the class value has been correctly filled, even if the service is created by a factory
            $class = $container->getParameterBag()->resolveValue($def->getClass());
            $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';

            if (!is_subclass_of($class, $interface)) {
                if (!class_exists($class, false)) {
                    throw new \InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
                }

                throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface));
            }

            $definition->addMethodCall('addSubscriberService', array($id, $class));
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * Event is the base class for classes containing event data.
 *
 * This class contains no event data. It is used by events that do not pass
 * state information to an event handler when an event is raised.
 *
 * You can call the method stopPropagation() to abort the execution of
 * further listeners in your event listener.
 *
 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
 * @author Jonathan Wage <jonwage@gmail.com>
 * @author Roman Borschel <roman@code-factory.org>
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class Event
{
    /**
     * @var bool Whether no further event listeners should be triggered
     */
    private $propagationStopped = false;

    /**
     * @var EventDispatcherInterface Dispatcher that dispatched this event
     */
    private $dispatcher;

    /**
     * @var string This event's name
     */
    private $name;

    /**
     * Returns whether further event listeners should be triggered.
     *
     * @see Event::stopPropagation()
     *
     * @return bool Whether propagation was already stopped for this event
     */
    public function isPropagationStopped()
    {
        return $this->propagationStopped;
    }

    /**
     * Stops the propagation of the event to further event listeners.
     *
     * If multiple event listeners are connected to the same event, no
     * further event listener will be triggered once any trigger calls
     * stopPropagation().
     */
    public function stopPropagation()
    {
        $this->propagationStopped = true;
    }

    /**
     * Stores the EventDispatcher that dispatches this Event.
     *
     * @param EventDispatcherInterface $dispatcher
     *
     * @deprecated since version 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call.
     */
    public function setDispatcher(EventDispatcherInterface $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    /**
     * Returns the EventDispatcher that dispatches this Event.
     *
     * @return EventDispatcherInterface
     *
     * @deprecated since version 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call.
     */
    public function getDispatcher()
    {
        @trigger_error('The '.__METHOD__.' method is deprecated since version 2.4 and will be removed in 3.0. The event dispatcher instance can be received in the listener call instead.', E_USER_DEPRECATED);

        return $this->dispatcher;
    }

    /**
     * Gets the event's name.
     *
     * @return string
     *
     * @deprecated since version 2.4, to be removed in 3.0. The event name is passed to the listener call.
     */
    public function getName()
    {
        @trigger_error('The '.__METHOD__.' method is deprecated since version 2.4 and will be removed in 3.0. The event name can be received in the listener call instead.', E_USER_DEPRECATED);

        return $this->name;
    }

    /**
     * Sets the event's name property.
     *
     * @param string $name The event name
     *
     * @deprecated since version 2.4, to be removed in 3.0. The event name is passed to the listener call.
     */
    public function setName($name)
    {
        $this->name = $name;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * The EventDispatcherInterface is the central point of Symfony's event listener system.
 *
 * Listeners are registered on the manager and events are dispatched through the
 * manager.
 *
 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
 * @author Jonathan Wage <jonwage@gmail.com>
 * @author Roman Borschel <roman@code-factory.org>
 * @author Bernhard Schussek <bschussek@gmail.com>
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Jordi Boggiano <j.boggiano@seld.be>
 * @author Jordan Alliot <jordan.alliot@gmail.com>
 */
class EventDispatcher implements EventDispatcherInterface
{
    private $listeners = array();
    private $sorted = array();

    /**
     * {@inheritdoc}
     */
    public function dispatch($eventName, Event $event = null)
    {
        if (null === $event) {
            $event = new Event();
        }

        $event->setDispatcher($this);
        $event->setName($eventName);

        if ($listeners = $this->getListeners($eventName)) {
            $this->doDispatch($listeners, $eventName, $event);
        }

        return $event;
    }

    /**
     * {@inheritdoc}
     */
    public function getListeners($eventName = null)
    {
        if (null !== $eventName) {
            if (!isset($this->listeners[$eventName])) {
                return array();
            }

            if (!isset($this->sorted[$eventName])) {
                $this->sortListeners($eventName);
            }

            return $this->sorted[$eventName];
        }

        foreach ($this->listeners as $eventName => $eventListeners) {
            if (!isset($this->sorted[$eventName])) {
                $this->sortListeners($eventName);
            }
        }

        return array_filter($this->sorted);
    }

    /**
     * Gets the listener priority for a specific event.
     *
     * Returns null if the event or the listener does not exist.
     *
     * @param string   $eventName The name of the event
     * @param callable $listener  The listener
     *
     * @return int|null The event listener priority
     */
    public function getListenerPriority($eventName, $listener)
    {
        if (!isset($this->listeners[$eventName])) {
            return;
        }

        foreach ($this->listeners[$eventName] as $priority => $listeners) {
            if (false !== in_array($listener, $listeners, true)) {
                return $priority;
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function hasListeners($eventName = null)
    {
        return (bool) $this->getListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function addListener($eventName, $listener, $priority = 0)
    {
        $this->listeners[$eventName][$priority][] = $listener;
        unset($this->sorted[$eventName]);
    }

    /**
     * {@inheritdoc}
     */
    public function removeListener($eventName, $listener)
    {
        if (!isset($this->listeners[$eventName])) {
            return;
        }

        foreach ($this->listeners[$eventName] as $priority => $listeners) {
            if (false !== ($key = array_search($listener, $listeners, true))) {
                unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]);
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function addSubscriber(EventSubscriberInterface $subscriber)
    {
        foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
            if (is_string($params)) {
                $this->addListener($eventName, array($subscriber, $params));
            } elseif (is_string($params[0])) {
                $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0);
            } else {
                foreach ($params as $listener) {
                    $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0);
                }
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function removeSubscriber(EventSubscriberInterface $subscriber)
    {
        foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
            if (is_array($params) && is_array($params[0])) {
                foreach ($params as $listener) {
                    $this->removeListener($eventName, array($subscriber, $listener[0]));
                }
            } else {
                $this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0]));
            }
        }
    }

    /**
     * Triggers the listeners of an event.
     *
     * This method can be overridden to add functionality that is executed
     * for each listener.
     *
     * @param callable[] $listeners The event listeners
     * @param string     $eventName The name of the event to dispatch
     * @param Event      $event     The event object to pass to the event handlers/listeners
     */
    protected function doDispatch($listeners, $eventName, Event $event)
    {
        foreach ($listeners as $listener) {
            if ($event->isPropagationStopped()) {
                break;
            }
            call_user_func($listener, $event, $eventName, $this);
        }
    }

    /**
     * Sorts the internal list of listeners for the given event by priority.
     *
     * @param string $eventName The name of the event
     */
    private function sortListeners($eventName)
    {
        krsort($this->listeners[$eventName]);
        $this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * The EventDispatcherInterface is the central point of Symfony's event listener system.
 * Listeners are registered on the manager and events are dispatched through the
 * manager.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
interface EventDispatcherInterface
{
    /**
     * Dispatches an event to all registered listeners.
     *
     * @param string $eventName The name of the event to dispatch. The name of
     *                          the event is the name of the method that is
     *                          invoked on listeners.
     * @param Event  $event     The event to pass to the event handlers/listeners
     *                          If not supplied, an empty Event instance is created.
     *
     * @return Event
     */
    public function dispatch($eventName, Event $event = null);

    /**
     * Adds an event listener that listens on the specified events.
     *
     * @param string   $eventName The event to listen on
     * @param callable $listener  The listener
     * @param int      $priority  The higher this value, the earlier an event
     *                            listener will be triggered in the chain (defaults to 0)
     */
    public function addListener($eventName, $listener, $priority = 0);

    /**
     * Adds an event subscriber.
     *
     * The subscriber is asked for all the events he is
     * interested in and added as a listener for these events.
     *
     * @param EventSubscriberInterface $subscriber The subscriber
     */
    public function addSubscriber(EventSubscriberInterface $subscriber);

    /**
     * Removes an event listener from the specified events.
     *
     * @param string   $eventName The event to remove a listener from
     * @param callable $listener  The listener to remove
     */
    public function removeListener($eventName, $listener);

    /**
     * Removes an event subscriber.
     *
     * @param EventSubscriberInterface $subscriber The subscriber
     */
    public function removeSubscriber(EventSubscriberInterface $subscriber);

    /**
     * Gets the listeners of a specific event or all listeners sorted by descending priority.
     *
     * @param string $eventName The name of the event
     *
     * @return array The event listeners for the specified event, or all event listeners by event name
     */
    public function getListeners($eventName = null);

    /**
     * Checks whether an event has any registered listeners.
     *
     * @param string $eventName The name of the event
     *
     * @return bool true if the specified event has any listeners, false otherwise
     */
    public function hasListeners($eventName = null);
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * An EventSubscriber knows himself what events he is interested in.
 * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes
 * {@link getSubscribedEvents} and registers the subscriber as a listener for all
 * returned events.
 *
 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
 * @author Jonathan Wage <jonwage@gmail.com>
 * @author Roman Borschel <roman@code-factory.org>
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
interface EventSubscriberInterface
{
    /**
     * Returns an array of event names this subscriber wants to listen to.
     *
     * The array keys are event names and the value can be:
     *
     *  * The method name to call (priority defaults to 0)
     *  * An array composed of the method name to call and the priority
     *  * An array of arrays composed of the method names to call and respective
     *    priorities, or 0 if unset
     *
     * For instance:
     *
     *  * array('eventName' => 'methodName')
     *  * array('eventName' => array('methodName', $priority))
     *  * array('eventName' => array(array('methodName1', $priority), array('methodName2')))
     *
     * @return array The event names to listen to
     */
    public static function getSubscribedEvents();
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * Event encapsulation class.
 *
 * Encapsulates events thus decoupling the observer from the subject they encapsulate.
 *
 * @author Drak <drak@zikula.org>
 */
class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
{
    /**
     * Event subject.
     *
     * @var mixed usually object or callable
     */
    protected $subject;

    /**
     * Array of arguments.
     *
     * @var array
     */
    protected $arguments;

    /**
     * Encapsulate an event with $subject and $args.
     *
     * @param mixed $subject   The subject of the event, usually an object
     * @param array $arguments Arguments to store in the event
     */
    public function __construct($subject = null, array $arguments = array())
    {
        $this->subject = $subject;
        $this->arguments = $arguments;
    }

    /**
     * Getter for subject property.
     *
     * @return mixed $subject The observer subject
     */
    public function getSubject()
    {
        return $this->subject;
    }

    /**
     * Get argument by key.
     *
     * @param string $key Key
     *
     * @return mixed Contents of array key
     *
     * @throws \InvalidArgumentException If key is not found.
     */
    public function getArgument($key)
    {
        if ($this->hasArgument($key)) {
            return $this->arguments[$key];
        }

        throw new \InvalidArgumentException(sprintf('Argument "%s" not found.', $key));
    }

    /**
     * Add argument to event.
     *
     * @param string $key   Argument name
     * @param mixed  $value Value
     *
     * @return $this
     */
    public function setArgument($key, $value)
    {
        $this->arguments[$key] = $value;

        return $this;
    }

    /**
     * Getter for all arguments.
     *
     * @return array
     */
    public function getArguments()
    {
        return $this->arguments;
    }

    /**
     * Set args property.
     *
     * @param array $args Arguments
     *
     * @return $this
     */
    public function setArguments(array $args = array())
    {
        $this->arguments = $args;

        return $this;
    }

    /**
     * Has argument.
     *
     * @param string $key Key of arguments array
     *
     * @return bool
     */
    public function hasArgument($key)
    {
        return array_key_exists($key, $this->arguments);
    }

    /**
     * ArrayAccess for argument getter.
     *
     * @param string $key Array key
     *
     * @return mixed
     *
     * @throws \InvalidArgumentException If key does not exist in $this->args.
     */
    public function offsetGet($key)
    {
        return $this->getArgument($key);
    }

    /**
     * ArrayAccess for argument setter.
     *
     * @param string $key   Array key to set
     * @param mixed  $value Value
     */
    public function offsetSet($key, $value)
    {
        $this->setArgument($key, $value);
    }

    /**
     * ArrayAccess for unset argument.
     *
     * @param string $key Array key
     */
    public function offsetUnset($key)
    {
        if ($this->hasArgument($key)) {
            unset($this->arguments[$key]);
        }
    }

    /**
     * ArrayAccess has argument.
     *
     * @param string $key Array key
     *
     * @return bool
     */
    public function offsetExists($key)
    {
        return $this->hasArgument($key);
    }

    /**
     * IteratorAggregate for iterating over the object like an array.
     *
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->arguments);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * A read-only proxy for an event dispatcher.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class ImmutableEventDispatcher implements EventDispatcherInterface
{
    /**
     * The proxied dispatcher.
     *
     * @var EventDispatcherInterface
     */
    private $dispatcher;

    /**
     * Creates an unmodifiable proxy for an event dispatcher.
     *
     * @param EventDispatcherInterface $dispatcher The proxied event dispatcher
     */
    public function __construct(EventDispatcherInterface $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    /**
     * {@inheritdoc}
     */
    public function dispatch($eventName, Event $event = null)
    {
        return $this->dispatcher->dispatch($eventName, $event);
    }

    /**
     * {@inheritdoc}
     */
    public function addListener($eventName, $listener, $priority = 0)
    {
        throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
    }

    /**
     * {@inheritdoc}
     */
    public function addSubscriber(EventSubscriberInterface $subscriber)
    {
        throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
    }

    /**
     * {@inheritdoc}
     */
    public function removeListener($eventName, $listener)
    {
        throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
    }

    /**
     * {@inheritdoc}
     */
    public function removeSubscriber(EventSubscriberInterface $subscriber)
    {
        throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
    }

    /**
     * {@inheritdoc}
     */
    public function getListeners($eventName = null)
    {
        return $this->dispatcher->getListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function getListenerPriority($eventName, $listener)
    {
        return $this->dispatcher->getListenerPriority($eventName, $listener);
    }

    /**
     * {@inheritdoc}
     */
    public function hasListeners($eventName = null)
    {
        return $this->dispatcher->hasListeners($eventName);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Parser;

/**
 * Validates YAML files syntax and outputs encountered errors.
 *
 * @author Grégoire Pineau <lyrixx@lyrixx.info>
 * @author Robin Chalas <robin.chalas@gmail.com>
 */
class LintCommand extends Command
{
    private $parser;
    private $format;
    private $displayCorrectFiles;
    private $directoryIteratorProvider;
    private $isReadableProvider;

    public function __construct($name = null, $directoryIteratorProvider = null, $isReadableProvider = null)
    {
        parent::__construct($name);

        $this->directoryIteratorProvider = $directoryIteratorProvider;
        $this->isReadableProvider = $isReadableProvider;
    }

    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this
            ->setName('lint:yaml')
            ->setDescription('Lints a file and outputs encountered errors')
            ->addArgument('filename', null, 'A file or a directory or STDIN')
            ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
            ->setHelp(<<<EOF
The <info>%command.name%</info> command lints a YAML file and outputs to STDOUT
the first encountered syntax error.

You can validates YAML contents passed from STDIN:

  <info>cat filename | php %command.full_name%</info>

You can also validate the syntax of a file:

  <info>php %command.full_name% filename</info>

Or of a whole directory:

  <info>php %command.full_name% dirname</info>
  <info>php %command.full_name% dirname --format=json</info>

EOF
            )
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $io = new SymfonyStyle($input, $output);
        $filename = $input->getArgument('filename');
        $this->format = $input->getOption('format');
        $this->displayCorrectFiles = $output->isVerbose();

        if (!$filename) {
            if (!$stdin = $this->getStdin()) {
                throw new \RuntimeException('Please provide a filename or pipe file content to STDIN.');
            }

            return $this->display($io, array($this->validate($stdin)));
        }

        if (!$this->isReadable($filename)) {
            throw new \RuntimeException(sprintf('File or directory "%s" is not readable.', $filename));
        }

        $filesInfo = array();
        foreach ($this->getFiles($filename) as $file) {
            $filesInfo[] = $this->validate(file_get_contents($file), $file);
        }

        return $this->display($io, $filesInfo);
    }

    private function validate($content, $file = null)
    {
        $prevErrorHandler = set_error_handler(function ($level, $message, $file, $line) use (&$prevErrorHandler) {
            if (E_USER_DEPRECATED === $level) {
                throw new ParseException($message);
            }

            return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false;
        });

        try {
            $this->getParser()->parse($content);
        } catch (ParseException $e) {
            return array('file' => $file, 'valid' => false, 'message' => $e->getMessage());
        } finally {
            restore_error_handler();
        }

        return array('file' => $file, 'valid' => true);
    }

    private function display(SymfonyStyle $io, array $files)
    {
        switch ($this->format) {
            case 'txt':
                return $this->displayTxt($io, $files);
            case 'json':
                return $this->displayJson($io, $files);
            default:
                throw new \InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format));
        }
    }

    private function displayTxt(SymfonyStyle $io, array $filesInfo)
    {
        $countFiles = count($filesInfo);
        $erroredFiles = 0;

        foreach ($filesInfo as $info) {
            if ($info['valid'] && $this->displayCorrectFiles) {
                $io->comment('<info>OK</info>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
            } elseif (!$info['valid']) {
                ++$erroredFiles;
                $io->text('<error> ERROR </error>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
                $io->text(sprintf('<error> >> %s</error>', $info['message']));
            }
        }

        if ($erroredFiles === 0) {
            $io->success(sprintf('All %d YAML files contain valid syntax.', $countFiles));
        } else {
            $io->warning(sprintf('%d YAML files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles));
        }

        return min($erroredFiles, 1);
    }

    private function displayJson(SymfonyStyle $io, array $filesInfo)
    {
        $errors = 0;

        array_walk($filesInfo, function (&$v) use (&$errors) {
            $v['file'] = (string) $v['file'];
            if (!$v['valid']) {
                ++$errors;
            }
        });

        $io->writeln(json_encode($filesInfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));

        return min($errors, 1);
    }

    private function getFiles($fileOrDirectory)
    {
        if (is_file($fileOrDirectory)) {
            yield new \SplFileInfo($fileOrDirectory);

            return;
        }

        foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) {
            if (!in_array($file->getExtension(), array('yml', 'yaml'))) {
                continue;
            }

            yield $file;
        }
    }

    private function getStdin()
    {
        if (0 !== ftell(STDIN)) {
            return;
        }

        $inputs = '';
        while (!feof(STDIN)) {
            $inputs .= fread(STDIN, 1024);
        }

        return $inputs;
    }

    private function getParser()
    {
        if (!$this->parser) {
            $this->parser = new Parser();
        }

        return $this->parser;
    }

    private function getDirectoryIterator($directory)
    {
        $default = function ($directory) {
            return new \RecursiveIteratorIterator(
                new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
                \RecursiveIteratorIterator::LEAVES_ONLY
            );
        };

        if (null !== $this->directoryIteratorProvider) {
            return call_user_func($this->directoryIteratorProvider, $directory, $default);
        }

        return $default($directory);
    }

    private function isReadable($fileOrDirectory)
    {
        $default = function ($fileOrDirectory) {
            return is_readable($fileOrDirectory);
        };

        if (null !== $this->isReadableProvider) {
            return call_user_func($this->isReadableProvider, $fileOrDirectory, $default);
        }

        return $default($fileOrDirectory);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml;

/**
 * Dumper dumps PHP variables to YAML strings.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Dumper
{
    /**
     * The amount of spaces to use for indentation of nested nodes.
     *
     * @var int
     */
    protected $indentation;

    /**
     * @param int $indentation
     */
    public function __construct($indentation = 4)
    {
        if ($indentation < 1) {
            throw new \InvalidArgumentException('The indentation must be greater than zero.');
        }

        $this->indentation = $indentation;
    }

    /**
     * Sets the indentation.
     *
     * @param int $num The amount of spaces to use for indentation of nested nodes
     *
     * @deprecated since version 3.1, to be removed in 4.0. Pass the indentation to the constructor instead.
     */
    public function setIndentation($num)
    {
        @trigger_error('The '.__METHOD__.' method is deprecated since version 3.1 and will be removed in 4.0. Pass the indentation to the constructor instead.', E_USER_DEPRECATED);

        $this->indentation = (int) $num;
    }

    /**
     * Dumps a PHP value to YAML.
     *
     * @param mixed $input  The PHP value
     * @param int   $inline The level where you switch to inline YAML
     * @param int   $indent The level of indentation (used internally)
     * @param int   $flags  A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
     *
     * @return string The YAML representation of the PHP value
     */
    public function dump($input, $inline = 0, $indent = 0, $flags = 0)
    {
        if (is_bool($flags)) {
            @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);

            if ($flags) {
                $flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE;
            } else {
                $flags = 0;
            }
        }

        if (func_num_args() >= 5) {
            @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', E_USER_DEPRECATED);

            if (func_get_arg(4)) {
                $flags |= Yaml::DUMP_OBJECT;
            }
        }

        $output = '';
        $prefix = $indent ? str_repeat(' ', $indent) : '';
        $dumpObjectAsInlineMap = true;

        if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($input instanceof \ArrayObject || $input instanceof \stdClass)) {
            $dumpObjectAsInlineMap = empty((array) $input);
        }

        if ($inline <= 0 || (!is_array($input) && $dumpObjectAsInlineMap) || empty($input)) {
            $output .= $prefix.Inline::dump($input, $flags);
        } else {
            $dumpAsMap = Inline::isHash($input);

            foreach ($input as $key => $value) {
                if ($inline >= 1 && Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && is_string($value) && false !== strpos($value, "\n")) {
                    $output .= sprintf("%s%s%s |\n", $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', '');

                    foreach (preg_split('/\n|\r\n/', $value) as $row) {
                        $output .= sprintf("%s%s%s\n", $prefix, str_repeat(' ', $this->indentation), $row);
                    }

                    continue;
                }

                $dumpObjectAsInlineMap = true;

                if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \ArrayObject || $value instanceof \stdClass)) {
                    $dumpObjectAsInlineMap = empty((array) $value);
                }

                $willBeInlined = $inline - 1 <= 0 || !is_array($value) && $dumpObjectAsInlineMap || empty($value);

                $output .= sprintf('%s%s%s%s',
                    $prefix,
                    $dumpAsMap ? Inline::dump($key, $flags).':' : '-',
                    $willBeInlined ? ' ' : "\n",
                    $this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags)
                ).($willBeInlined ? "\n" : '');
            }
        }

        return $output;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml;

/**
 * Escaper encapsulates escaping rules for single and double-quoted
 * YAML strings.
 *
 * @author Matthew Lewinski <matthew@lewinski.org>
 *
 * @internal
 */
class Escaper
{
    // Characters that would cause a dumped string to require double quoting.
    const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9";

    // Mapping arrays for escaping a double quoted string. The backslash is
    // first to ensure proper escaping because str_replace operates iteratively
    // on the input arrays. This ordering of the characters avoids the use of strtr,
    // which performs more slowly.
    private static $escapees = array('\\', '\\\\', '\\"', '"',
                                     "\x00",  "\x01",  "\x02",  "\x03",  "\x04",  "\x05",  "\x06",  "\x07",
                                     "\x08",  "\x09",  "\x0a",  "\x0b",  "\x0c",  "\x0d",  "\x0e",  "\x0f",
                                     "\x10",  "\x11",  "\x12",  "\x13",  "\x14",  "\x15",  "\x16",  "\x17",
                                     "\x18",  "\x19",  "\x1a",  "\x1b",  "\x1c",  "\x1d",  "\x1e",  "\x1f",
                                     "\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9",
                               );
    private static $escaped = array('\\\\', '\\"', '\\\\', '\\"',
                                     '\\0',   '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a',
                                     '\\b',   '\\t',   '\\n',   '\\v',   '\\f',   '\\r',   '\\x0e', '\\x0f',
                                     '\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17',
                                     '\\x18', '\\x19', '\\x1a', '\\e',   '\\x1c', '\\x1d', '\\x1e', '\\x1f',
                                     '\\N', '\\_', '\\L', '\\P',
                              );

    /**
     * Determines if a PHP value would require double quoting in YAML.
     *
     * @param string $value A PHP value
     *
     * @return bool True if the value would require double quotes
     */
    public static function requiresDoubleQuoting($value)
    {
        return 0 < preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value);
    }

    /**
     * Escapes and surrounds a PHP value with double quotes.
     *
     * @param string $value A PHP value
     *
     * @return string The quoted, escaped string
     */
    public static function escapeWithDoubleQuotes($value)
    {
        return sprintf('"%s"', str_replace(self::$escapees, self::$escaped, $value));
    }

    /**
     * Determines if a PHP value would require single quoting in YAML.
     *
     * @param string $value A PHP value
     *
     * @return bool True if the value would require single quotes
     */
    public static function requiresSingleQuoting($value)
    {
        // Determines if a PHP value is entirely composed of a value that would
        // require single quoting in YAML.
        if (in_array(strtolower($value), array('null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'))) {
            return true;
        }

        // Determines if the PHP value contains any single characters that would
        // cause it to require single quoting in YAML.
        return 0 < preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` ]/x', $value);
    }

    /**
     * Escapes and surrounds a PHP value with single quotes.
     *
     * @param string $value A PHP value
     *
     * @return string The quoted, escaped string
     */
    public static function escapeWithSingleQuotes($value)
    {
        return sprintf("'%s'", str_replace('\'', '\'\'', $value));
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml\Exception;

/**
 * Exception class thrown when an error occurs during dumping.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class DumpException extends RuntimeException
{
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml\Exception;

/**
 * Exception interface for all exceptions thrown by the component.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface ExceptionInterface
{
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml\Exception;

/**
 * Exception class thrown when an error occurs during parsing.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ParseException extends RuntimeException
{
    private $parsedFile;
    private $parsedLine;
    private $snippet;
    private $rawMessage;

    /**
     * Constructor.
     *
     * @param string          $message    The error message
     * @param int             $parsedLine The line where the error occurred
     * @param string|null     $snippet    The snippet of code near the problem
     * @param string|null     $parsedFile The file name where the error occurred
     * @param \Exception|null $previous   The previous exception
     */
    public function __construct($message, $parsedLine = -1, $snippet = null, $parsedFile = null, \Exception $previous = null)
    {
        $this->parsedFile = $parsedFile;
        $this->parsedLine = $parsedLine;
        $this->snippet = $snippet;
        $this->rawMessage = $message;

        $this->updateRepr();

        parent::__construct($this->message, 0, $previous);
    }

    /**
     * Gets the snippet of code near the error.
     *
     * @return string The snippet of code
     */
    public function getSnippet()
    {
        return $this->snippet;
    }

    /**
     * Sets the snippet of code near the error.
     *
     * @param string $snippet The code snippet
     */
    public function setSnippet($snippet)
    {
        $this->snippet = $snippet;

        $this->updateRepr();
    }

    /**
     * Gets the filename where the error occurred.
     *
     * This method returns null if a string is parsed.
     *
     * @return string The filename
     */
    public function getParsedFile()
    {
        return $this->parsedFile;
    }

    /**
     * Sets the filename where the error occurred.
     *
     * @param string $parsedFile The filename
     */
    public function setParsedFile($parsedFile)
    {
        $this->parsedFile = $parsedFile;

        $this->updateRepr();
    }

    /**
     * Gets the line where the error occurred.
     *
     * @return int The file line
     */
    public function getParsedLine()
    {
        return $this->parsedLine;
    }

    /**
     * Sets the line where the error occurred.
     *
     * @param int $parsedLine The file line
     */
    public function setParsedLine($parsedLine)
    {
        $this->parsedLine = $parsedLine;

        $this->updateRepr();
    }

    private function updateRepr()
    {
        $this->message = $this->rawMessage;

        $dot = false;
        if ('.' === substr($this->message, -1)) {
            $this->message = substr($this->message, 0, -1);
            $dot = true;
        }

        if (null !== $this->parsedFile) {
            $this->message .= sprintf(' in %s', json_encode($this->parsedFile, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
        }

        if ($this->parsedLine >= 0) {
            $this->message .= sprintf(' at line %d', $this->parsedLine);
        }

        if ($this->snippet) {
            $this->message .= sprintf(' (near "%s")', $this->snippet);
        }

        if ($dot) {
            $this->message .= '.';
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml\Exception;

/**
 * Exception class thrown when an error occurs during parsing.
 *
 * @author Romain Neutron <imprec@gmail.com>
 */
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml;

use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Exception\DumpException;
use Symfony\Component\Yaml\Tag\TaggedValue;

/**
 * Inline implements a YAML parser/dumper for the YAML inline syntax.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @internal
 */
class Inline
{
    const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')';

    public static $parsedLineNumber;

    private static $exceptionOnInvalidType = false;
    private static $objectSupport = false;
    private static $objectForMap = false;
    private static $constantSupport = false;

    /**
     * Converts a YAML string to a PHP value.
     *
     * @param string $value      A YAML string
     * @param int    $flags      A bit field of PARSE_* constants to customize the YAML parser behavior
     * @param array  $references Mapping of variable names to values
     *
     * @return mixed A PHP value
     *
     * @throws ParseException
     */
    public static function parse($value, $flags = 0, $references = array())
    {
        if (is_bool($flags)) {
            @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);

            if ($flags) {
                $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE;
            } else {
                $flags = 0;
            }
        }

        if (func_num_args() >= 3 && !is_array($references)) {
            @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED);

            if ($references) {
                $flags |= Yaml::PARSE_OBJECT;
            }

            if (func_num_args() >= 4) {
                @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED);

                if (func_get_arg(3)) {
                    $flags |= Yaml::PARSE_OBJECT_FOR_MAP;
                }
            }

            if (func_num_args() >= 5) {
                $references = func_get_arg(4);
            } else {
                $references = array();
            }
        }

        self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags);
        self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags);
        self::$objectForMap = (bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags);
        self::$constantSupport = (bool) (Yaml::PARSE_CONSTANT & $flags);

        $value = trim($value);

        if ('' === $value) {
            return '';
        }

        if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) {
            $mbEncoding = mb_internal_encoding();
            mb_internal_encoding('ASCII');
        }

        $i = 0;
        $tag = self::parseTag($value, $i, $flags);
        switch ($value[$i]) {
            case '[':
                $result = self::parseSequence($value, $flags, $i, $references);
                ++$i;
                break;
            case '{':
                $result = self::parseMapping($value, $flags, $i, $references);
                ++$i;
                break;
            default:
                $result = self::parseScalar($value, $flags, null, $i, null === $tag, $references);
        }

        if (null !== $tag) {
            return new TaggedValue($tag, $result);
        }

        // some comments are allowed at the end
        if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) {
            throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)));
        }

        if (isset($mbEncoding)) {
            mb_internal_encoding($mbEncoding);
        }

        return $result;
    }

    /**
     * Dumps a given PHP variable to a YAML string.
     *
     * @param mixed $value The PHP variable to convert
     * @param int   $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
     *
     * @return string The YAML string representing the PHP value
     *
     * @throws DumpException When trying to dump PHP resource
     */
    public static function dump($value, $flags = 0)
    {
        if (is_bool($flags)) {
            @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);

            if ($flags) {
                $flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE;
            } else {
                $flags = 0;
            }
        }

        if (func_num_args() >= 3) {
            @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', E_USER_DEPRECATED);

            if (func_get_arg(2)) {
                $flags |= Yaml::DUMP_OBJECT;
            }
        }

        switch (true) {
            case is_resource($value):
                if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) {
                    throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value)));
                }

                return 'null';
            case $value instanceof \DateTimeInterface:
                return $value->format('c');
            case is_object($value):
                if ($value instanceof TaggedValue) {
                    return '!'.$value->getTag().' '.self::dump($value->getValue(), $flags);
                }

                if (Yaml::DUMP_OBJECT & $flags) {
                    return '!php/object:'.serialize($value);
                }

                if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) {
                    return self::dumpArray($value, $flags & ~Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
                }

                if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) {
                    throw new DumpException('Object support when dumping a YAML file has been disabled.');
                }

                return 'null';
            case is_array($value):
                return self::dumpArray($value, $flags);
            case null === $value:
                return 'null';
            case true === $value:
                return 'true';
            case false === $value:
                return 'false';
            case ctype_digit($value):
                return is_string($value) ? "'$value'" : (int) $value;
            case is_numeric($value):
                $locale = setlocale(LC_NUMERIC, 0);
                if (false !== $locale) {
                    setlocale(LC_NUMERIC, 'C');
                }
                if (is_float($value)) {
                    $repr = (string) $value;
                    if (is_infinite($value)) {
                        $repr = str_ireplace('INF', '.Inf', $repr);
                    } elseif (floor($value) == $value && $repr == $value) {
                        // Preserve float data type since storing a whole number will result in integer value.
                        $repr = '!!float '.$repr;
                    }
                } else {
                    $repr = is_string($value) ? "'$value'" : (string) $value;
                }
                if (false !== $locale) {
                    setlocale(LC_NUMERIC, $locale);
                }

                return $repr;
            case '' == $value:
                return "''";
            case self::isBinaryString($value):
                return '!!binary '.base64_encode($value);
            case Escaper::requiresDoubleQuoting($value):
                return Escaper::escapeWithDoubleQuotes($value);
            case Escaper::requiresSingleQuoting($value):
            case Parser::preg_match('{^[0-9]+[_0-9]*$}', $value):
            case Parser::preg_match(self::getHexRegex(), $value):
            case Parser::preg_match(self::getTimestampRegex(), $value):
                return Escaper::escapeWithSingleQuotes($value);
            default:
                return $value;
        }
    }

    /**
     * Check if given array is hash or just normal indexed array.
     *
     * @internal
     *
     * @param array|\ArrayObject|\stdClass $value The PHP array or array-like object to check
     *
     * @return bool true if value is hash array, false otherwise
     */
    public static function isHash($value)
    {
        if ($value instanceof \stdClass || $value instanceof \ArrayObject) {
            return true;
        }

        $expectedKey = 0;

        foreach ($value as $key => $val) {
            if ($key !== $expectedKey++) {
                return true;
            }
        }

        return false;
    }

    /**
     * Dumps a PHP array to a YAML string.
     *
     * @param array $value The PHP array to dump
     * @param int   $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
     *
     * @return string The YAML string representing the PHP array
     */
    private static function dumpArray($value, $flags)
    {
        // array
        if (($value || Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE & $flags) && !self::isHash($value)) {
            $output = array();
            foreach ($value as $val) {
                $output[] = self::dump($val, $flags);
            }

            return sprintf('[%s]', implode(', ', $output));
        }

        // hash
        $output = array();
        foreach ($value as $key => $val) {
            $output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags));
        }

        return sprintf('{ %s }', implode(', ', $output));
    }

    /**
     * Parses a YAML scalar.
     *
     * @param string   $scalar
     * @param int      $flags
     * @param string[] $delimiters
     * @param int      &$i
     * @param bool     $evaluate
     * @param array    $references
     *
     * @return string
     *
     * @throws ParseException When malformed inline YAML string is parsed
     *
     * @internal
     */
    public static function parseScalar($scalar, $flags = 0, $delimiters = null, &$i = 0, $evaluate = true, $references = array(), $legacyOmittedKeySupport = false)
    {
        if (in_array($scalar[$i], array('"', "'"))) {
            // quoted scalar
            $output = self::parseQuotedScalar($scalar, $i);

            if (null !== $delimiters) {
                $tmp = ltrim(substr($scalar, $i), ' ');
                if (!in_array($tmp[0], $delimiters)) {
                    throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)));
                }
            }
        } else {
            // "normal" string
            if (!$delimiters) {
                $output = substr($scalar, $i);
                $i += strlen($output);

                // remove comments
                if (Parser::preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
                    $output = substr($output, 0, $match[0][1]);
                }
            } elseif (Parser::preg_match('/^(.'.($legacyOmittedKeySupport ? '+' : '*').'?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
                $output = $match[1];
                $i += strlen($output);
            } else {
                throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar));
            }

            // a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >)
            if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) {
                throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]));
            }

            if ($output && '%' === $output[0]) {
                @trigger_error(sprintf('Not quoting the scalar "%s" starting with the "%%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.', $output), E_USER_DEPRECATED);
            }

            if ($evaluate) {
                $output = self::evaluateScalar($output, $flags, $references);
            }
        }

        return $output;
    }

    /**
     * Parses a YAML quoted scalar.
     *
     * @param string $scalar
     * @param int    &$i
     *
     * @return string
     *
     * @throws ParseException When malformed inline YAML string is parsed
     */
    private static function parseQuotedScalar($scalar, &$i)
    {
        if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
            throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i)));
        }

        $output = substr($match[0], 1, strlen($match[0]) - 2);

        $unescaper = new Unescaper();
        if ('"' == $scalar[$i]) {
            $output = $unescaper->unescapeDoubleQuotedString($output);
        } else {
            $output = $unescaper->unescapeSingleQuotedString($output);
        }

        $i += strlen($match[0]);

        return $output;
    }

    /**
     * Parses a YAML sequence.
     *
     * @param string $sequence
     * @param int    $flags
     * @param int    &$i
     * @param array  $references
     *
     * @return array
     *
     * @throws ParseException When malformed inline YAML string is parsed
     */
    private static function parseSequence($sequence, $flags, &$i = 0, $references = array())
    {
        $output = array();
        $len = strlen($sequence);
        ++$i;

        // [foo, bar, ...]
        while ($i < $len) {
            if (']' === $sequence[$i]) {
                return $output;
            }
            if (',' === $sequence[$i] || ' ' === $sequence[$i]) {
                ++$i;

                continue;
            }

            $tag = self::parseTag($sequence, $i, $flags);
            switch ($sequence[$i]) {
                case '[':
                    // nested sequence
                    $value = self::parseSequence($sequence, $flags, $i, $references);
                    break;
                case '{':
                    // nested mapping
                    $value = self::parseMapping($sequence, $flags, $i, $references);
                    break;
                default:
                    $isQuoted = in_array($sequence[$i], array('"', "'"));
                    $value = self::parseScalar($sequence, $flags, array(',', ']'), $i, null === $tag, $references);

                    // the value can be an array if a reference has been resolved to an array var
                    if (is_string($value) && !$isQuoted && false !== strpos($value, ': ')) {
                        // embedded mapping?
                        try {
                            $pos = 0;
                            $value = self::parseMapping('{'.$value.'}', $flags, $pos, $references);
                        } catch (\InvalidArgumentException $e) {
                            // no, it's not
                        }
                    }

                    --$i;
            }

            if (null !== $tag) {
                $value = new TaggedValue($tag, $value);
            }

            $output[] = $value;

            ++$i;
        }

        throw new ParseException(sprintf('Malformed inline YAML string: %s.', $sequence));
    }

    /**
     * Parses a YAML mapping.
     *
     * @param string $mapping
     * @param int    $flags
     * @param int    &$i
     * @param array  $references
     *
     * @return array|\stdClass
     *
     * @throws ParseException When malformed inline YAML string is parsed
     */
    private static function parseMapping($mapping, $flags, &$i = 0, $references = array())
    {
        $output = array();
        $len = strlen($mapping);
        ++$i;

        // {foo: bar, bar:foo, ...}
        while ($i < $len) {
            switch ($mapping[$i]) {
                case ' ':
                case ',':
                    ++$i;
                    continue 2;
                case '}':
                    if (self::$objectForMap) {
                        return (object) $output;
                    }

                    return $output;
            }

            // key
            $isKeyQuoted = in_array($mapping[$i], array('"', "'"), true);
            $key = self::parseScalar($mapping, $flags, array(':', ' '), $i, false, array(), true);

            if (':' !== $key && false === $i = strpos($mapping, ':', $i)) {
                break;
            }

            if (':' === $key) {
                @trigger_error('Omitting the key of a mapping is deprecated and will throw a ParseException in 4.0.', E_USER_DEPRECATED);
            }

            if (!(Yaml::PARSE_KEYS_AS_STRINGS & $flags)) {
                $evaluatedKey = self::evaluateScalar($key, $flags, $references);

                if ('' !== $key && $evaluatedKey !== $key && !is_string($evaluatedKey)) {
                    @trigger_error('Implicit casting of incompatible mapping keys to strings is deprecated since version 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Pass the PARSE_KEYS_AS_STRING flag to explicitly enable the type casts.', E_USER_DEPRECATED);
                }
            }

            if (':' !== $key && !$isKeyQuoted && (!isset($mapping[$i + 1]) || !in_array($mapping[$i + 1], array(' ', ',', '[', ']', '{', '}'), true))) {
                @trigger_error('Using a colon after an unquoted mapping key that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}") is deprecated since version 3.2 and will throw a ParseException in 4.0.', E_USER_DEPRECATED);
            }

            while ($i < $len) {
                if (':' === $mapping[$i] || ' ' === $mapping[$i]) {
                    ++$i;

                    continue;
                }

                $tag = self::parseTag($mapping, $i, $flags);
                $duplicate = false;
                switch ($mapping[$i]) {
                    case '[':
                        // nested sequence
                        $value = self::parseSequence($mapping, $flags, $i, $references);
                        // Spec: Keys MUST be unique; first one wins.
                        // Parser cannot abort this mapping earlier, since lines
                        // are processed sequentially.
                        if (isset($output[$key])) {
                            @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, self::$parsedLineNumber + 1), E_USER_DEPRECATED);
                            $duplicate = true;
                        }
                        break;
                    case '{':
                        // nested mapping
                        $value = self::parseMapping($mapping, $flags, $i, $references);
                        // Spec: Keys MUST be unique; first one wins.
                        // Parser cannot abort this mapping earlier, since lines
                        // are processed sequentially.
                        if (isset($output[$key])) {
                            @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, self::$parsedLineNumber + 1), E_USER_DEPRECATED);
                            $duplicate = true;
                        }
                        break;
                    default:
                        $value = self::parseScalar($mapping, $flags, array(',', '}'), $i, null === $tag, $references);
                        // Spec: Keys MUST be unique; first one wins.
                        // Parser cannot abort this mapping earlier, since lines
                        // are processed sequentially.
                        if (isset($output[$key])) {
                            @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, self::$parsedLineNumber + 1), E_USER_DEPRECATED);
                            $duplicate = true;
                        }
                        --$i;
                }

                if (!$duplicate) {
                    if (null !== $tag) {
                        $output[$key] = new TaggedValue($tag, $value);
                    } else {
                        $output[$key] = $value;
                    }
                }
                ++$i;

                continue 2;
            }
        }

        throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping));
    }

    /**
     * Evaluates scalars and replaces magic values.
     *
     * @param string $scalar
     * @param int    $flags
     * @param array  $references
     *
     * @return mixed The evaluated YAML string
     *
     * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved
     */
    private static function evaluateScalar($scalar, $flags, $references = array())
    {
        $scalar = trim($scalar);
        $scalarLower = strtolower($scalar);

        if (0 === strpos($scalar, '*')) {
            if (false !== $pos = strpos($scalar, '#')) {
                $value = substr($scalar, 1, $pos - 2);
            } else {
                $value = substr($scalar, 1);
            }

            // an unquoted *
            if (false === $value || '' === $value) {
                throw new ParseException('A reference must contain at least one character.');
            }

            if (!array_key_exists($value, $references)) {
                throw new ParseException(sprintf('Reference "%s" does not exist.', $value));
            }

            return $references[$value];
        }

        switch (true) {
            case 'null' === $scalarLower:
            case '' === $scalar:
            case '~' === $scalar:
                return;
            case 'true' === $scalarLower:
                return true;
            case 'false' === $scalarLower:
                return false;
            case $scalar[0] === '!':
                switch (true) {
                    case 0 === strpos($scalar, '!str'):
                        return (string) substr($scalar, 5);
                    case 0 === strpos($scalar, '! '):
                        return (int) self::parseScalar(substr($scalar, 2), $flags);
                    case 0 === strpos($scalar, '!php/object:'):
                        if (self::$objectSupport) {
                            return unserialize(substr($scalar, 12));
                        }

                        if (self::$exceptionOnInvalidType) {
                            throw new ParseException('Object support when parsing a YAML file has been disabled.');
                        }

                        return;
                    case 0 === strpos($scalar, '!!php/object:'):
                        if (self::$objectSupport) {
                            @trigger_error('The !!php/object tag to indicate dumped PHP objects is deprecated since version 3.1 and will be removed in 4.0. Use the !php/object tag instead.', E_USER_DEPRECATED);

                            return unserialize(substr($scalar, 13));
                        }

                        if (self::$exceptionOnInvalidType) {
                            throw new ParseException('Object support when parsing a YAML file has been disabled.');
                        }

                        return;
                    case 0 === strpos($scalar, '!php/const:'):
                        if (self::$constantSupport) {
                            if (defined($const = substr($scalar, 11))) {
                                return constant($const);
                            }

                            throw new ParseException(sprintf('The constant "%s" is not defined.', $const));
                        }
                        if (self::$exceptionOnInvalidType) {
                            throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar));
                        }

                        return;
                    case 0 === strpos($scalar, '!!float '):
                        return (float) substr($scalar, 8);
                    case 0 === strpos($scalar, '!!binary '):
                        return self::evaluateBinaryScalar(substr($scalar, 9));
                    default:
                        @trigger_error(sprintf('Using the unquoted scalar value "%s" is deprecated since version 3.3 and will be considered as a tagged value in 4.0. You must quote it.', $scalar), E_USER_DEPRECATED);
                }

            // Optimize for returning strings.
            case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || is_numeric($scalar[0]):
                switch (true) {
                    case Parser::preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar):
                        $scalar = str_replace('_', '', (string) $scalar);
                        // omitting the break / return as integers are handled in the next case
                    case ctype_digit($scalar):
                        $raw = $scalar;
                        $cast = (int) $scalar;

                        return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
                    case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)):
                        $raw = $scalar;
                        $cast = (int) $scalar;

                        return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw);
                    case is_numeric($scalar):
                    case Parser::preg_match(self::getHexRegex(), $scalar):
                        $scalar = str_replace('_', '', $scalar);

                        return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar;
                    case '.inf' === $scalarLower:
                    case '.nan' === $scalarLower:
                        return -log(0);
                    case '-.inf' === $scalarLower:
                        return log(0);
                    case Parser::preg_match('/^(-|\+)?[0-9][0-9,]*(\.[0-9_]+)?$/', $scalar):
                    case Parser::preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar):
                        if (false !== strpos($scalar, ',')) {
                            @trigger_error('Using the comma as a group separator for floats is deprecated since version 3.2 and will be removed in 4.0.', E_USER_DEPRECATED);
                        }

                        return (float) str_replace(array(',', '_'), '', $scalar);
                    case Parser::preg_match(self::getTimestampRegex(), $scalar):
                        if (Yaml::PARSE_DATETIME & $flags) {
                            // When no timezone is provided in the parsed date, YAML spec says we must assume UTC.
                            return new \DateTime($scalar, new \DateTimeZone('UTC'));
                        }

                        $timeZone = date_default_timezone_get();
                        date_default_timezone_set('UTC');
                        $time = strtotime($scalar);
                        date_default_timezone_set($timeZone);

                        return $time;
                }
        }

        return (string) $scalar;
    }

    /**
     * @param string $value
     * @param int    &$i
     * @param int    $flags
     *
     * @return null|string
     */
    private static function parseTag($value, &$i, $flags)
    {
        if ('!' !== $value[$i]) {
            return;
        }

        $tagLength = strcspn($value, " \t\n", $i + 1);
        $tag = substr($value, $i + 1, $tagLength);

        $nextOffset = $i + $tagLength + 1;
        $nextOffset += strspn($value, ' ', $nextOffset);

        // Is followed by a scalar
        if (!isset($value[$nextOffset]) || !in_array($value[$nextOffset], array('[', '{'), true)) {
            // Manage scalars in {@link self::evaluateScalar()}
            return;
        }

        // Built-in tags
        if ($tag && '!' === $tag[0]) {
            throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag));
        }

        if (Yaml::PARSE_CUSTOM_TAGS & $flags) {
            $i = $nextOffset;

            return $tag;
        }

        throw new ParseException(sprintf('Tags support is not enabled. Enable the `Yaml::PARSE_CUSTOM_TAGS` flag to use "!%s".', $tag));
    }

    /**
     * @param string $scalar
     *
     * @return string
     *
     * @internal
     */
    public static function evaluateBinaryScalar($scalar)
    {
        $parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar));

        if (0 !== (strlen($parsedBinaryData) % 4)) {
            throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', strlen($parsedBinaryData)));
        }

        if (!Parser::preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) {
            throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData));
        }

        return base64_decode($parsedBinaryData, true);
    }

    private static function isBinaryString($value)
    {
        return !preg_match('//u', $value) || preg_match('/[^\x00\x07-\x0d\x1B\x20-\xff]/', $value);
    }

    /**
     * Gets a regex that matches a YAML date.
     *
     * @return string The regular expression
     *
     * @see http://www.yaml.org/spec/1.2/spec.html#id2761573
     */
    private static function getTimestampRegex()
    {
        return <<<EOF
        ~^
        (?P<year>[0-9][0-9][0-9][0-9])
        -(?P<month>[0-9][0-9]?)
        -(?P<day>[0-9][0-9]?)
        (?:(?:[Tt]|[ \t]+)
        (?P<hour>[0-9][0-9]?)
        :(?P<minute>[0-9][0-9])
        :(?P<second>[0-9][0-9])
        (?:\.(?P<fraction>[0-9]*))?
        (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
        (?::(?P<tz_minute>[0-9][0-9]))?))?)?
        $~x
EOF;
    }

    /**
     * Gets a regex that matches a YAML number in hexadecimal notation.
     *
     * @return string
     */
    private static function getHexRegex()
    {
        return '~^0x[0-9a-f_]++$~i';
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml;

use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Tag\TaggedValue;

/**
 * Parser parses YAML strings to convert them to PHP arrays.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Parser
{
    const TAG_PATTERN = '(?P<tag>![\w!.\/:-]+)';
    const BLOCK_SCALAR_HEADER_PATTERN = '(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments> +#.*)?';

    private $offset = 0;
    private $totalNumberOfLines;
    private $lines = array();
    private $currentLineNb = -1;
    private $currentLine = '';
    private $refs = array();
    private $skippedLineNumbers = array();
    private $locallySkippedLineNumbers = array();

    public function __construct()
    {
        if (func_num_args() > 0) {
            @trigger_error(sprintf('The constructor arguments $offset, $totalNumberOfLines, $skippedLineNumbers of %s are deprecated and will be removed in 4.0', self::class), E_USER_DEPRECATED);

            $this->offset = func_get_arg(0);
            if (func_num_args() > 1) {
                $this->totalNumberOfLines = func_get_arg(1);
            }
            if (func_num_args() > 2) {
                $this->skippedLineNumbers = func_get_arg(2);
            }
        }
    }

    /**
     * Parses a YAML string to a PHP value.
     *
     * @param string $value A YAML string
     * @param int    $flags A bit field of PARSE_* constants to customize the YAML parser behavior
     *
     * @return mixed A PHP value
     *
     * @throws ParseException If the YAML is not valid
     */
    public function parse($value, $flags = 0)
    {
        if (is_bool($flags)) {
            @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);

            if ($flags) {
                $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE;
            } else {
                $flags = 0;
            }
        }

        if (func_num_args() >= 3) {
            @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED);

            if (func_get_arg(2)) {
                $flags |= Yaml::PARSE_OBJECT;
            }
        }

        if (func_num_args() >= 4) {
            @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED);

            if (func_get_arg(3)) {
                $flags |= Yaml::PARSE_OBJECT_FOR_MAP;
            }
        }

        if (false === preg_match('//u', $value)) {
            throw new ParseException('The YAML value does not appear to be valid UTF-8.');
        }

        $this->refs = array();

        $mbEncoding = null;
        $e = null;
        $data = null;

        if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) {
            $mbEncoding = mb_internal_encoding();
            mb_internal_encoding('UTF-8');
        }

        try {
            $data = $this->doParse($value, $flags);
        } catch (\Exception $e) {
        } catch (\Throwable $e) {
        }

        if (null !== $mbEncoding) {
            mb_internal_encoding($mbEncoding);
        }

        $this->lines = array();
        $this->currentLine = '';
        $this->refs = array();
        $this->skippedLineNumbers = array();
        $this->locallySkippedLineNumbers = array();

        if (null !== $e) {
            throw $e;
        }

        return $data;
    }

    private function doParse($value, $flags)
    {
        $this->currentLineNb = -1;
        $this->currentLine = '';
        $value = $this->cleanup($value);
        $this->lines = explode("\n", $value);
        $this->locallySkippedLineNumbers = array();

        if (null === $this->totalNumberOfLines) {
            $this->totalNumberOfLines = count($this->lines);
        }

        if (!$this->moveToNextLine()) {
            return null;
        }

        $data = array();
        $context = null;
        $allowOverwrite = false;

        while ($this->isCurrentLineEmpty()) {
            if (!$this->moveToNextLine()) {
                return null;
            }
        }

        // Resolves the tag and returns if end of the document
        if (null !== ($tag = $this->getLineTag($this->currentLine, $flags, false)) && !$this->moveToNextLine()) {
            return new TaggedValue($tag, '');
        }

        do {
            if ($this->isCurrentLineEmpty()) {
                continue;
            }

            // tab?
            if ("\t" === $this->currentLine[0]) {
                throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
            }

            $isRef = $mergeNode = false;
            if (self::preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+))?$#u', rtrim($this->currentLine), $values)) {
                if ($context && 'mapping' == $context) {
                    throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine);
                }
                $context = 'sequence';

                if (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
                    $isRef = $matches['ref'];
                    $values['value'] = $matches['value'];
                }

                if (isset($values['value'][1]) && '?' === $values['value'][0] && ' ' === $values['value'][1]) {
                    @trigger_error('Starting an unquoted string with a question mark followed by a space is deprecated since version 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', E_USER_DEPRECATED);
                }

                // array
                if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
                    $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags);
                } elseif (null !== $subTag = $this->getLineTag(ltrim($values['value'], ' '), $flags)) {
                    $data[] = new TaggedValue(
                        $subTag,
                        $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags)
                    );
                } else {
                    if (isset($values['leadspaces'])
                        && self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $this->trimTag($values['value']), $matches)
                    ) {
                        // this is a compact notation element, add to next block and parse
                        $block = $values['value'];
                        if ($this->isNextLineIndented()) {
                            $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + strlen($values['leadspaces']) + 1);
                        }

                        $data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block, $flags);
                    } else {
                        $data[] = $this->parseValue($values['value'], $flags, $context);
                    }
                }
                if ($isRef) {
                    $this->refs[$isRef] = end($data);
                }
            } elseif (
                self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?(?:![^\s]++\s++)?[^ \'"\[\{!].*?) *\:(\s++(?P<value>.+))?$#u', rtrim($this->currentLine), $values)
                && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'")))
            ) {
                if ($context && 'sequence' == $context) {
                    throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine);
                }
                $context = 'mapping';

                // force correct settings
                Inline::parse(null, $flags, $this->refs);
                try {
                    Inline::$parsedLineNumber = $this->getRealCurrentLineNb();
                    $i = 0;
                    $evaluateKey = !(Yaml::PARSE_KEYS_AS_STRINGS & $flags);

                    // constants in key will be evaluated anyway
                    if (isset($values['key'][0]) && '!' === $values['key'][0] && Yaml::PARSE_CONSTANT & $flags) {
                        $evaluateKey = true;
                    }

                    $key = Inline::parseScalar($values['key'], 0, null, $i, $evaluateKey);
                } catch (ParseException $e) {
                    $e->setParsedLine($this->getRealCurrentLineNb() + 1);
                    $e->setSnippet($this->currentLine);

                    throw $e;
                }

                if (!(Yaml::PARSE_KEYS_AS_STRINGS & $flags) && !is_string($key)) {
                    $keyType = is_numeric($key) ? 'numeric key' : 'incompatible key type';
                    @trigger_error(sprintf('Implicit casting of %s to string on line %d is deprecated since version 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Pass the PARSE_KEYS_AS_STRING flag to explicitly enable the type casts.', $keyType, $this->getRealCurrentLineNb()), E_USER_DEPRECATED);
                }

                // Convert float keys to strings, to avoid being converted to integers by PHP
                if (is_float($key)) {
                    $key = (string) $key;
                }

                if ('<<' === $key) {
                    $mergeNode = true;
                    $allowOverwrite = true;
                    if (isset($values['value']) && 0 === strpos($values['value'], '*')) {
                        $refName = substr(rtrim($values['value']), 1);
                        if (!array_key_exists($refName, $this->refs)) {
                            throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine);
                        }

                        $refValue = $this->refs[$refName];

                        if (!is_array($refValue)) {
                            throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
                        }

                        $data += $refValue; // array union
                    } else {
                        if (isset($values['value']) && $values['value'] !== '') {
                            $value = $values['value'];
                        } else {
                            $value = $this->getNextEmbedBlock();
                        }
                        $parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags);

                        if (!is_array($parsed)) {
                            throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
                        }

                        if (isset($parsed[0])) {
                            // If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes
                            // and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier
                            // in the sequence override keys specified in later mapping nodes.
                            foreach ($parsed as $parsedItem) {
                                if (!is_array($parsedItem)) {
                                    throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem);
                                }

                                $data += $parsedItem; // array union
                            }
                        } else {
                            // If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the
                            // current mapping, unless the key already exists in it.
                            $data += $parsed; // array union
                        }
                    }
                } elseif (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]++) *+(?P<value>.*)#u', $values['value'], $matches)) {
                    $isRef = $matches['ref'];
                    $values['value'] = $matches['value'];
                }

                $subTag = null;
                if ($mergeNode) {
                    // Merge keys
                } elseif (!isset($values['value']) || '' === $values['value'] || 0 === strpos($values['value'], '#') || (null !== $subTag = $this->getLineTag($values['value'], $flags))) {
                    // hash
                    // if next line is less indented or equal, then it means that the current value is null
                    if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) {
                        // Spec: Keys MUST be unique; first one wins.
                        // But overwriting is allowed when a merge node is used in current block.
                        if ($allowOverwrite || !isset($data[$key])) {
                            if (null !== $subTag) {
                                $data[$key] = new TaggedValue($subTag, '');
                            } else {
                                $data[$key] = null;
                            }
                        } else {
                            @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED);
                        }
                    } else {
                        // remember the parsed line number here in case we need it to provide some contexts in error messages below
                        $realCurrentLineNbKey = $this->getRealCurrentLineNb();
                        $value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $flags);
                        // Spec: Keys MUST be unique; first one wins.
                        // But overwriting is allowed when a merge node is used in current block.
                        if ($allowOverwrite || !isset($data[$key])) {
                            if (null !== $subTag) {
                                $data[$key] = new TaggedValue($subTag, $value);
                            } else {
                                $data[$key] = $value;
                            }
                        } else {
                            @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, $realCurrentLineNbKey + 1), E_USER_DEPRECATED);
                        }
                    }
                } else {
                    $value = $this->parseValue(rtrim($values['value']), $flags, $context);
                    // Spec: Keys MUST be unique; first one wins.
                    // But overwriting is allowed when a merge node is used in current block.
                    if ($allowOverwrite || !isset($data[$key])) {
                        $data[$key] = $value;
                    } else {
                        @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED);
                    }
                }
                if ($isRef) {
                    $this->refs[$isRef] = $data[$key];
                }
            } else {
                // multiple documents are not supported
                if ('---' === $this->currentLine) {
                    throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine);
                }

                if (isset($this->currentLine[1]) && '?' === $this->currentLine[0] && ' ' === $this->currentLine[1]) {
                    @trigger_error('Starting an unquoted string with a question mark followed by a space is deprecated since version 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', E_USER_DEPRECATED);
                }

                // 1-liner optionally followed by newline(s)
                if (is_string($value) && $this->lines[0] === trim($value)) {
                    try {
                        Inline::$parsedLineNumber = $this->getRealCurrentLineNb();
                        $value = Inline::parse($this->lines[0], $flags, $this->refs);
                    } catch (ParseException $e) {
                        $e->setParsedLine($this->getRealCurrentLineNb() + 1);
                        $e->setSnippet($this->currentLine);

                        throw $e;
                    }

                    return $value;
                }

                // try to parse the value as a multi-line string as a last resort
                if (0 === $this->currentLineNb) {
                    $parseError = false;
                    $previousLineWasNewline = false;
                    $value = '';

                    foreach ($this->lines as $line) {
                        try {
                            if (isset($line[0]) && ('"' === $line[0] || "'" === $line[0])) {
                                $parsedLine = $line;
                            } else {
                                $parsedLine = Inline::parse($line, $flags, $this->refs);
                            }

                            if (!is_string($parsedLine)) {
                                $parseError = true;
                                break;
                            }

                            if ('' === trim($parsedLine)) {
                                $value .= "\n";
                                $previousLineWasNewline = true;
                            } elseif ($previousLineWasNewline) {
                                $value .= trim($parsedLine);
                                $previousLineWasNewline = false;
                            } else {
                                $value .= ' '.trim($parsedLine);
                                $previousLineWasNewline = false;
                            }
                        } catch (ParseException $e) {
                            $parseError = true;
                            break;
                        }
                    }

                    if (!$parseError) {
                        return trim($value);
                    }
                }

                throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
            }
        } while ($this->moveToNextLine());

        if (null !== $tag) {
            $data = new TaggedValue($tag, $data);
        }

        if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && !is_object($data) && 'mapping' === $context) {
            $object = new \stdClass();

            foreach ($data as $key => $value) {
                $object->$key = $value;
            }

            $data = $object;
        }

        return empty($data) ? null : $data;
    }

    private function parseBlock($offset, $yaml, $flags)
    {
        $skippedLineNumbers = $this->skippedLineNumbers;

        foreach ($this->locallySkippedLineNumbers as $lineNumber) {
            if ($lineNumber < $offset) {
                continue;
            }

            $skippedLineNumbers[] = $lineNumber;
        }

        $parser = new self();
        $parser->offset = $offset;
        $parser->totalNumberOfLines = $this->totalNumberOfLines;
        $parser->skippedLineNumbers = $skippedLineNumbers;
        $parser->refs = &$this->refs;

        return $parser->doParse($yaml, $flags);
    }

    /**
     * Returns the current line number (takes the offset into account).
     *
     * @return int The current line number
     */
    private function getRealCurrentLineNb()
    {
        $realCurrentLineNumber = $this->currentLineNb + $this->offset;

        foreach ($this->skippedLineNumbers as $skippedLineNumber) {
            if ($skippedLineNumber > $realCurrentLineNumber) {
                break;
            }

            ++$realCurrentLineNumber;
        }

        return $realCurrentLineNumber;
    }

    /**
     * Returns the current line indentation.
     *
     * @return int The current line indentation
     */
    private function getCurrentLineIndentation()
    {
        return strlen($this->currentLine) - strlen(ltrim($this->currentLine, ' '));
    }

    /**
     * Returns the next embed block of YAML.
     *
     * @param int  $indentation The indent level at which the block is to be read, or null for default
     * @param bool $inSequence  True if the enclosing data structure is a sequence
     *
     * @return string A YAML string
     *
     * @throws ParseException When indentation problem are detected
     */
    private function getNextEmbedBlock($indentation = null, $inSequence = false)
    {
        $oldLineIndentation = $this->getCurrentLineIndentation();
        $blockScalarIndentations = array();

        if ($this->isBlockScalarHeader()) {
            $blockScalarIndentations[] = $oldLineIndentation;
        }

        if (!$this->moveToNextLine()) {
            return;
        }

        if (null === $indentation) {
            $newIndent = $this->getCurrentLineIndentation();

            $unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem();

            if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) {
                throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
            }
        } else {
            $newIndent = $indentation;
        }

        $data = array();
        if ($this->getCurrentLineIndentation() >= $newIndent) {
            $data[] = substr($this->currentLine, $newIndent);
        } else {
            $this->moveToPreviousLine();

            return;
        }

        if ($inSequence && $oldLineIndentation === $newIndent && isset($data[0][0]) && '-' === $data[0][0]) {
            // the previous line contained a dash but no item content, this line is a sequence item with the same indentation
            // and therefore no nested list or mapping
            $this->moveToPreviousLine();

            return;
        }

        $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem();

        if (empty($blockScalarIndentations) && $this->isBlockScalarHeader()) {
            $blockScalarIndentations[] = $this->getCurrentLineIndentation();
        }

        $previousLineIndentation = $this->getCurrentLineIndentation();

        while ($this->moveToNextLine()) {
            $indent = $this->getCurrentLineIndentation();

            // terminate all block scalars that are more indented than the current line
            if (!empty($blockScalarIndentations) && $indent < $previousLineIndentation && trim($this->currentLine) !== '') {
                foreach ($blockScalarIndentations as $key => $blockScalarIndentation) {
                    if ($blockScalarIndentation >= $indent) {
                        unset($blockScalarIndentations[$key]);
                    }
                }
            }

            if (empty($blockScalarIndentations) && !$this->isCurrentLineComment() && $this->isBlockScalarHeader()) {
                $blockScalarIndentations[] = $indent;
            }

            $previousLineIndentation = $indent;

            if ($isItUnindentedCollection && !$this->isCurrentLineEmpty() && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) {
                $this->moveToPreviousLine();
                break;
            }

            if ($this->isCurrentLineBlank()) {
                $data[] = substr($this->currentLine, $newIndent);
                continue;
            }

            // we ignore "comment" lines only when we are not inside a scalar block
            if (empty($blockScalarIndentations) && $this->isCurrentLineComment()) {
                // remember ignored comment lines (they are used later in nested
                // parser calls to determine real line numbers)
                //
                // CAUTION: beware to not populate the global property here as it
                // will otherwise influence the getRealCurrentLineNb() call here
                // for consecutive comment lines and subsequent embedded blocks
                $this->locallySkippedLineNumbers[] = $this->getRealCurrentLineNb();

                continue;
            }

            if ($indent >= $newIndent) {
                $data[] = substr($this->currentLine, $newIndent);
            } elseif (0 == $indent) {
                $this->moveToPreviousLine();

                break;
            } else {
                throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
            }
        }

        return implode("\n", $data);
    }

    /**
     * Moves the parser to the next line.
     *
     * @return bool
     */
    private function moveToNextLine()
    {
        if ($this->currentLineNb >= count($this->lines) - 1) {
            return false;
        }

        $this->currentLine = $this->lines[++$this->currentLineNb];

        return true;
    }

    /**
     * Moves the parser to the previous line.
     *
     * @return bool
     */
    private function moveToPreviousLine()
    {
        if ($this->currentLineNb < 1) {
            return false;
        }

        $this->currentLine = $this->lines[--$this->currentLineNb];

        return true;
    }

    /**
     * Parses a YAML value.
     *
     * @param string $value   A YAML value
     * @param int    $flags   A bit field of PARSE_* constants to customize the YAML parser behavior
     * @param string $context The parser context (either sequence or mapping)
     *
     * @return mixed A PHP value
     *
     * @throws ParseException When reference does not exist
     */
    private function parseValue($value, $flags, $context)
    {
        if (0 === strpos($value, '*')) {
            if (false !== $pos = strpos($value, '#')) {
                $value = substr($value, 1, $pos - 2);
            } else {
                $value = substr($value, 1);
            }

            if (!array_key_exists($value, $this->refs)) {
                throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine);
            }

            return $this->refs[$value];
        }

        if (self::preg_match('/^(?:'.self::TAG_PATTERN.' +)?'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) {
            $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : '';

            $data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers));

            if ('' !== $matches['tag']) {
                if ('!!binary' === $matches['tag']) {
                    return Inline::evaluateBinaryScalar($data);
                } elseif ('!' !== $matches['tag']) {
                    @trigger_error(sprintf('Using the custom tag "%s" for the value "%s" is deprecated since version 3.3. It will be replaced by an instance of %s in 4.0.', $matches['tag'], $data, TaggedValue::class), E_USER_DEPRECATED);
                }
            }

            return $data;
        }

        try {
            $quotation = '' !== $value && ('"' === $value[0] || "'" === $value[0]) ? $value[0] : null;

            // do not take following lines into account when the current line is a quoted single line value
            if (null !== $quotation && self::preg_match('/^'.$quotation.'.*'.$quotation.'(\s*#.*)?$/', $value)) {
                return Inline::parse($value, $flags, $this->refs);
            }

            while ($this->moveToNextLine()) {
                // unquoted strings end before the first unindented line
                if (null === $quotation && $this->getCurrentLineIndentation() === 0) {
                    $this->moveToPreviousLine();

                    break;
                }

                $value .= ' '.trim($this->currentLine);

                // quoted string values end with a line that is terminated with the quotation character
                if ('' !== $this->currentLine && substr($this->currentLine, -1) === $quotation) {
                    break;
                }
            }

            Inline::$parsedLineNumber = $this->getRealCurrentLineNb();
            $parsedValue = Inline::parse($value, $flags, $this->refs);

            if ('mapping' === $context && is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) {
                throw new ParseException('A colon cannot be used in an unquoted mapping value.');
            }

            return $parsedValue;
        } catch (ParseException $e) {
            $e->setParsedLine($this->getRealCurrentLineNb() + 1);
            $e->setSnippet($this->currentLine);

            throw $e;
        }
    }

    /**
     * Parses a block scalar.
     *
     * @param string $style       The style indicator that was used to begin this block scalar (| or >)
     * @param string $chomping    The chomping indicator that was used to begin this block scalar (+ or -)
     * @param int    $indentation The indentation indicator that was used to begin this block scalar
     *
     * @return string The text value
     */
    private function parseBlockScalar($style, $chomping = '', $indentation = 0)
    {
        $notEOF = $this->moveToNextLine();
        if (!$notEOF) {
            return '';
        }

        $isCurrentLineBlank = $this->isCurrentLineBlank();
        $blockLines = array();

        // leading blank lines are consumed before determining indentation
        while ($notEOF && $isCurrentLineBlank) {
            // newline only if not EOF
            if ($notEOF = $this->moveToNextLine()) {
                $blockLines[] = '';
                $isCurrentLineBlank = $this->isCurrentLineBlank();
            }
        }

        // determine indentation if not specified
        if (0 === $indentation) {
            if (self::preg_match('/^ +/', $this->currentLine, $matches)) {
                $indentation = strlen($matches[0]);
            }
        }

        if ($indentation > 0) {
            $pattern = sprintf('/^ {%d}(.*)$/', $indentation);

            while (
                $notEOF && (
                    $isCurrentLineBlank ||
                    self::preg_match($pattern, $this->currentLine, $matches)
                )
            ) {
                if ($isCurrentLineBlank && strlen($this->currentLine) > $indentation) {
                    $blockLines[] = substr($this->currentLine, $indentation);
                } elseif ($isCurrentLineBlank) {
                    $blockLines[] = '';
                } else {
                    $blockLines[] = $matches[1];
                }

                // newline only if not EOF
                if ($notEOF = $this->moveToNextLine()) {
                    $isCurrentLineBlank = $this->isCurrentLineBlank();
                }
            }
        } elseif ($notEOF) {
            $blockLines[] = '';
        }

        if ($notEOF) {
            $blockLines[] = '';
            $this->moveToPreviousLine();
        } elseif (!$notEOF && !$this->isCurrentLineLastLineInDocument()) {
            $blockLines[] = '';
        }

        // folded style
        if ('>' === $style) {
            $text = '';
            $previousLineIndented = false;
            $previousLineBlank = false;

            for ($i = 0, $blockLinesCount = count($blockLines); $i < $blockLinesCount; ++$i) {
                if ('' === $blockLines[$i]) {
                    $text .= "\n";
                    $previousLineIndented = false;
                    $previousLineBlank = true;
                } elseif (' ' === $blockLines[$i][0]) {
                    $text .= "\n".$blockLines[$i];
                    $previousLineIndented = true;
                    $previousLineBlank = false;
                } elseif ($previousLineIndented) {
                    $text .= "\n".$blockLines[$i];
                    $previousLineIndented = false;
                    $previousLineBlank = false;
                } elseif ($previousLineBlank || 0 === $i) {
                    $text .= $blockLines[$i];
                    $previousLineIndented = false;
                    $previousLineBlank = false;
                } else {
                    $text .= ' '.$blockLines[$i];
                    $previousLineIndented = false;
                    $previousLineBlank = false;
                }
            }
        } else {
            $text = implode("\n", $blockLines);
        }

        // deal with trailing newlines
        if ('' === $chomping) {
            $text = preg_replace('/\n+$/', "\n", $text);
        } elseif ('-' === $chomping) {
            $text = preg_replace('/\n+$/', '', $text);
        }

        return $text;
    }

    /**
     * Returns true if the next line is indented.
     *
     * @return bool Returns true if the next line is indented, false otherwise
     */
    private function isNextLineIndented()
    {
        $currentIndentation = $this->getCurrentLineIndentation();
        $EOF = !$this->moveToNextLine();

        while (!$EOF && $this->isCurrentLineEmpty()) {
            $EOF = !$this->moveToNextLine();
        }

        if ($EOF) {
            return false;
        }

        $ret = $this->getCurrentLineIndentation() > $currentIndentation;

        $this->moveToPreviousLine();

        return $ret;
    }

    /**
     * Returns true if the current line is blank or if it is a comment line.
     *
     * @return bool Returns true if the current line is empty or if it is a comment line, false otherwise
     */
    private function isCurrentLineEmpty()
    {
        return $this->isCurrentLineBlank() || $this->isCurrentLineComment();
    }

    /**
     * Returns true if the current line is blank.
     *
     * @return bool Returns true if the current line is blank, false otherwise
     */
    private function isCurrentLineBlank()
    {
        return '' == trim($this->currentLine, ' ');
    }

    /**
     * Returns true if the current line is a comment line.
     *
     * @return bool Returns true if the current line is a comment line, false otherwise
     */
    private function isCurrentLineComment()
    {
        //checking explicitly the first char of the trim is faster than loops or strpos
        $ltrimmedLine = ltrim($this->currentLine, ' ');

        return '' !== $ltrimmedLine && $ltrimmedLine[0] === '#';
    }

    private function isCurrentLineLastLineInDocument()
    {
        return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1);
    }

    /**
     * Cleanups a YAML string to be parsed.
     *
     * @param string $value The input YAML string
     *
     * @return string A cleaned up YAML string
     */
    private function cleanup($value)
    {
        $value = str_replace(array("\r\n", "\r"), "\n", $value);

        // strip YAML header
        $count = 0;
        $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u', '', $value, -1, $count);
        $this->offset += $count;

        // remove leading comments
        $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count);
        if ($count == 1) {
            // items have been removed, update the offset
            $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
            $value = $trimmedValue;
        }

        // remove start of the document marker (---)
        $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count);
        if ($count == 1) {
            // items have been removed, update the offset
            $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
            $value = $trimmedValue;

            // remove end of the document marker (...)
            $value = preg_replace('#\.\.\.\s*$#', '', $value);
        }

        return $value;
    }

    /**
     * Returns true if the next line starts unindented collection.
     *
     * @return bool Returns true if the next line starts unindented collection, false otherwise
     */
    private function isNextLineUnIndentedCollection()
    {
        $currentIndentation = $this->getCurrentLineIndentation();
        $notEOF = $this->moveToNextLine();

        while ($notEOF && $this->isCurrentLineEmpty()) {
            $notEOF = $this->moveToNextLine();
        }

        if (false === $notEOF) {
            return false;
        }

        $ret = $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem();

        $this->moveToPreviousLine();

        return $ret;
    }

    /**
     * Returns true if the string is un-indented collection item.
     *
     * @return bool Returns true if the string is un-indented collection item, false otherwise
     */
    private function isStringUnIndentedCollectionItem()
    {
        return '-' === rtrim($this->currentLine) || 0 === strpos($this->currentLine, '- ');
    }

    /**
     * Tests whether or not the current line is the header of a block scalar.
     *
     * @return bool
     */
    private function isBlockScalarHeader()
    {
        return (bool) self::preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine);
    }

    /**
     * A local wrapper for `preg_match` which will throw a ParseException if there
     * is an internal error in the PCRE engine.
     *
     * This avoids us needing to check for "false" every time PCRE is used
     * in the YAML engine
     *
     * @throws ParseException on a PCRE internal error
     *
     * @see preg_last_error()
     *
     * @internal
     */
    public static function preg_match($pattern, $subject, &$matches = null, $flags = 0, $offset = 0)
    {
        if (false === $ret = preg_match($pattern, $subject, $matches, $flags, $offset)) {
            switch (preg_last_error()) {
                case PREG_INTERNAL_ERROR:
                    $error = 'Internal PCRE error.';
                    break;
                case PREG_BACKTRACK_LIMIT_ERROR:
                    $error = 'pcre.backtrack_limit reached.';
                    break;
                case PREG_RECURSION_LIMIT_ERROR:
                    $error = 'pcre.recursion_limit reached.';
                    break;
                case PREG_BAD_UTF8_ERROR:
                    $error = 'Malformed UTF-8 data.';
                    break;
                case PREG_BAD_UTF8_OFFSET_ERROR:
                    $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.';
                    break;
                default:
                    $error = 'Error.';
            }

            throw new ParseException($error);
        }

        return $ret;
    }

    /**
     * Trim the tag on top of the value.
     *
     * Prevent values such as `!foo {quz: bar}` to be considered as
     * a mapping block.
     */
    private function trimTag($value)
    {
        if ('!' === $value[0]) {
            return ltrim(substr($value, 1, strcspn($value, " \r\n", 1)), ' ');
        }

        return $value;
    }

    private function getLineTag($value, $flags, $nextLineCheck = true)
    {
        if ('' === $value || '!' !== $value[0] || 1 !== self::preg_match('/^'.self::TAG_PATTERN.' *( +#.*)?$/', $value, $matches)) {
            return;
        }

        if ($nextLineCheck && !$this->isNextLineIndented()) {
            return;
        }

        $tag = substr($matches['tag'], 1);

        // Built-in tags
        if ($tag && '!' === $tag[0]) {
            throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag));
        }

        if (Yaml::PARSE_CUSTOM_TAGS & $flags) {
            return $tag;
        }

        throw new ParseException(sprintf('Tags support is not enabled. You must use the flag `Yaml::PARSE_CUSTOM_TAGS` to use "%s".', $matches['tag']));
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml\Tag;

/**
 * @author Nicolas Grekas <p@tchwork.com>
 * @author Guilhem N. <egetick@gmail.com>
 */
final class TaggedValue
{
    private $tag;
    private $value;

    /**
     * @param string $tag
     * @param mixed  $value
     */
    public function __construct($tag, $value)
    {
        $this->tag = $tag;
        $this->value = $value;
    }

    /**
     * @return string
     */
    public function getTag()
    {
        return $this->tag;
    }

    /**
     * @return mixed
     */
    public function getValue()
    {
        return $this->value;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml;

use Symfony\Component\Yaml\Exception\ParseException;

/**
 * Unescaper encapsulates unescaping rules for single and double-quoted
 * YAML strings.
 *
 * @author Matthew Lewinski <matthew@lewinski.org>
 *
 * @internal
 */
class Unescaper
{
    /**
     * Regex fragment that matches an escaped character in a double quoted string.
     */
    const REGEX_ESCAPED_CHARACTER = '\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)';

    /**
     * Unescapes a single quoted string.
     *
     * @param string $value A single quoted string
     *
     * @return string The unescaped string
     */
    public function unescapeSingleQuotedString($value)
    {
        return str_replace('\'\'', '\'', $value);
    }

    /**
     * Unescapes a double quoted string.
     *
     * @param string $value A double quoted string
     *
     * @return string The unescaped string
     */
    public function unescapeDoubleQuotedString($value)
    {
        $callback = function ($match) {
            return $this->unescapeCharacter($match[0]);
        };

        // evaluate the string
        return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value);
    }

    /**
     * Unescapes a character that was found in a double-quoted string.
     *
     * @param string $value An escaped character
     *
     * @return string The unescaped character
     */
    private function unescapeCharacter($value)
    {
        switch ($value[1]) {
            case '0':
                return "\x0";
            case 'a':
                return "\x7";
            case 'b':
                return "\x8";
            case 't':
                return "\t";
            case "\t":
                return "\t";
            case 'n':
                return "\n";
            case 'v':
                return "\xB";
            case 'f':
                return "\xC";
            case 'r':
                return "\r";
            case 'e':
                return "\x1B";
            case ' ':
                return ' ';
            case '"':
                return '"';
            case '/':
                return '/';
            case '\\':
                return '\\';
            case 'N':
                // U+0085 NEXT LINE
                return "\xC2\x85";
            case '_':
                // U+00A0 NO-BREAK SPACE
                return "\xC2\xA0";
            case 'L':
                // U+2028 LINE SEPARATOR
                return "\xE2\x80\xA8";
            case 'P':
                // U+2029 PARAGRAPH SEPARATOR
                return "\xE2\x80\xA9";
            case 'x':
                return self::utf8chr(hexdec(substr($value, 2, 2)));
            case 'u':
                return self::utf8chr(hexdec(substr($value, 2, 4)));
            case 'U':
                return self::utf8chr(hexdec(substr($value, 2, 8)));
            default:
                throw new ParseException(sprintf('Found unknown escape character "%s".', $value));
        }
    }

    /**
     * Get the UTF-8 character for the given code point.
     *
     * @param int $c The unicode code point
     *
     * @return string The corresponding UTF-8 character
     */
    private static function utf8chr($c)
    {
        if (0x80 > $c %= 0x200000) {
            return chr($c);
        }
        if (0x800 > $c) {
            return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
        }
        if (0x10000 > $c) {
            return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
        }

        return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml;

use Symfony\Component\Yaml\Exception\ParseException;

/**
 * Yaml offers convenience methods to load and dump YAML.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Yaml
{
    const DUMP_OBJECT = 1;
    const PARSE_EXCEPTION_ON_INVALID_TYPE = 2;
    const PARSE_OBJECT = 4;
    const PARSE_OBJECT_FOR_MAP = 8;
    const DUMP_EXCEPTION_ON_INVALID_TYPE = 16;
    const PARSE_DATETIME = 32;
    const DUMP_OBJECT_AS_MAP = 64;
    const DUMP_MULTI_LINE_LITERAL_BLOCK = 128;
    const PARSE_CONSTANT = 256;
    const PARSE_CUSTOM_TAGS = 512;
    const DUMP_EMPTY_ARRAY_AS_SEQUENCE = 1024;
    const PARSE_KEYS_AS_STRINGS = 2048;

    /**
     * Parses YAML into a PHP value.
     *
     *  Usage:
     *  <code>
     *   $array = Yaml::parse(file_get_contents('config.yml'));
     *   print_r($array);
     *  </code>
     *
     * @param string $input A string containing YAML
     * @param int    $flags A bit field of PARSE_* constants to customize the YAML parser behavior
     *
     * @return mixed The YAML converted to a PHP value
     *
     * @throws ParseException If the YAML is not valid
     */
    public static function parse($input, $flags = 0)
    {
        if (is_bool($flags)) {
            @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);

            if ($flags) {
                $flags = self::PARSE_EXCEPTION_ON_INVALID_TYPE;
            } else {
                $flags = 0;
            }
        }

        if (func_num_args() >= 3) {
            @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the PARSE_OBJECT flag instead.', E_USER_DEPRECATED);

            if (func_get_arg(2)) {
                $flags |= self::PARSE_OBJECT;
            }
        }

        if (func_num_args() >= 4) {
            @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED);

            if (func_get_arg(3)) {
                $flags |= self::PARSE_OBJECT_FOR_MAP;
            }
        }

        $yaml = new Parser();

        return $yaml->parse($input, $flags);
    }

    /**
     * Dumps a PHP value to a YAML string.
     *
     * The dump method, when supplied with an array, will do its best
     * to convert the array into friendly YAML.
     *
     * @param mixed $input  The PHP value
     * @param int   $inline The level where you switch to inline YAML
     * @param int   $indent The amount of spaces to use for indentation of nested nodes
     * @param int   $flags  A bit field of DUMP_* constants to customize the dumped YAML string
     *
     * @return string A YAML string representing the original PHP value
     */
    public static function dump($input, $inline = 2, $indent = 4, $flags = 0)
    {
        if (is_bool($flags)) {
            @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);

            if ($flags) {
                $flags = self::DUMP_EXCEPTION_ON_INVALID_TYPE;
            } else {
                $flags = 0;
            }
        }

        if (func_num_args() >= 5) {
            @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the DUMP_OBJECT flag instead.', E_USER_DEPRECATED);

            if (func_get_arg(4)) {
                $flags |= self::DUMP_OBJECT;
            }
        }

        $yaml = new Dumper($indent);

        return $yaml->dump($input, $inline, 0, $flags);
    }
}
<?php

/*
 * This file is part of the webmozart/assert package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Assert;

use BadMethodCallException;
use InvalidArgumentException;
use Traversable;
use Exception;
use Throwable;
use Closure;

/**
 * Efficient assertions to validate the input/output of your methods.
 *
 * @method static void nullOrString($value, $message = '')
 * @method static void nullOrStringNotEmpty($value, $message = '')
 * @method static void nullOrInteger($value, $message = '')
 * @method static void nullOrIntegerish($value, $message = '')
 * @method static void nullOrFloat($value, $message = '')
 * @method static void nullOrNumeric($value, $message = '')
 * @method static void nullOrBoolean($value, $message = '')
 * @method static void nullOrScalar($value, $message = '')
 * @method static void nullOrObject($value, $message = '')
 * @method static void nullOrResource($value, $type = null, $message = '')
 * @method static void nullOrIsCallable($value, $message = '')
 * @method static void nullOrIsArray($value, $message = '')
 * @method static void nullOrIsTraversable($value, $message = '')
 * @method static void nullOrIsInstanceOf($value, $class, $message = '')
 * @method static void nullOrNotInstanceOf($value, $class, $message = '')
 * @method static void nullOrIsEmpty($value, $message = '')
 * @method static void nullOrNotEmpty($value, $message = '')
 * @method static void nullOrTrue($value, $message = '')
 * @method static void nullOrFalse($value, $message = '')
 * @method static void nullOrEq($value, $value2, $message = '')
 * @method static void nullOrNotEq($value,$value2,  $message = '')
 * @method static void nullOrSame($value, $value2, $message = '')
 * @method static void nullOrNotSame($value, $value2, $message = '')
 * @method static void nullOrGreaterThan($value, $value2, $message = '')
 * @method static void nullOrGreaterThanEq($value, $value2, $message = '')
 * @method static void nullOrLessThan($value, $value2, $message = '')
 * @method static void nullOrLessThanEq($value, $value2, $message = '')
 * @method static void nullOrRange($value, $min, $max, $message = '')
 * @method static void nullOrOneOf($value, $values, $message = '')
 * @method static void nullOrContains($value, $subString, $message = '')
 * @method static void nullOrStartsWith($value, $prefix, $message = '')
 * @method static void nullOrStartsWithLetter($value, $message = '')
 * @method static void nullOrEndsWith($value, $suffix, $message = '')
 * @method static void nullOrRegex($value, $pattern, $message = '')
 * @method static void nullOrAlpha($value, $message = '')
 * @method static void nullOrDigits($value, $message = '')
 * @method static void nullOrAlnum($value, $message = '')
 * @method static void nullOrLower($value, $message = '')
 * @method static void nullOrUpper($value, $message = '')
 * @method static void nullOrLength($value, $length, $message = '')
 * @method static void nullOrMinLength($value, $min, $message = '')
 * @method static void nullOrMaxLength($value, $max, $message = '')
 * @method static void nullOrLengthBetween($value, $min, $max, $message = '')
 * @method static void nullOrFileExists($value, $message = '')
 * @method static void nullOrFile($value, $message = '')
 * @method static void nullOrDirectory($value, $message = '')
 * @method static void nullOrReadable($value, $message = '')
 * @method static void nullOrWritable($value, $message = '')
 * @method static void nullOrClassExists($value, $message = '')
 * @method static void nullOrSubclassOf($value, $class, $message = '')
 * @method static void nullOrImplementsInterface($value, $interface, $message = '')
 * @method static void nullOrPropertyExists($value, $property, $message = '')
 * @method static void nullOrPropertyNotExists($value, $property, $message = '')
 * @method static void nullOrMethodExists($value, $method, $message = '')
 * @method static void nullOrMethodNotExists($value, $method, $message = '')
 * @method static void nullOrKeyExists($value, $key, $message = '')
 * @method static void nullOrKeyNotExists($value, $key, $message = '')
 * @method static void nullOrCount($value, $key, $message = '')
 * @method static void nullOrUuid($values, $message = '')
 * @method static void allString($values, $message = '')
 * @method static void allStringNotEmpty($values, $message = '')
 * @method static void allInteger($values, $message = '')
 * @method static void allIntegerish($values, $message = '')
 * @method static void allFloat($values, $message = '')
 * @method static void allNumeric($values, $message = '')
 * @method static void allBoolean($values, $message = '')
 * @method static void allScalar($values, $message = '')
 * @method static void allObject($values, $message = '')
 * @method static void allResource($values, $type = null, $message = '')
 * @method static void allIsCallable($values, $message = '')
 * @method static void allIsArray($values, $message = '')
 * @method static void allIsTraversable($values, $message = '')
 * @method static void allIsInstanceOf($values, $class, $message = '')
 * @method static void allNotInstanceOf($values, $class, $message = '')
 * @method static void allNull($values, $message = '')
 * @method static void allNotNull($values, $message = '')
 * @method static void allIsEmpty($values, $message = '')
 * @method static void allNotEmpty($values, $message = '')
 * @method static void allTrue($values, $message = '')
 * @method static void allFalse($values, $message = '')
 * @method static void allEq($values, $value2, $message = '')
 * @method static void allNotEq($values,$value2,  $message = '')
 * @method static void allSame($values, $value2, $message = '')
 * @method static void allNotSame($values, $value2, $message = '')
 * @method static void allGreaterThan($values, $value2, $message = '')
 * @method static void allGreaterThanEq($values, $value2, $message = '')
 * @method static void allLessThan($values, $value2, $message = '')
 * @method static void allLessThanEq($values, $value2, $message = '')
 * @method static void allRange($values, $min, $max, $message = '')
 * @method static void allOneOf($values, $values, $message = '')
 * @method static void allContains($values, $subString, $message = '')
 * @method static void allStartsWith($values, $prefix, $message = '')
 * @method static void allStartsWithLetter($values, $message = '')
 * @method static void allEndsWith($values, $suffix, $message = '')
 * @method static void allRegex($values, $pattern, $message = '')
 * @method static void allAlpha($values, $message = '')
 * @method static void allDigits($values, $message = '')
 * @method static void allAlnum($values, $message = '')
 * @method static void allLower($values, $message = '')
 * @method static void allUpper($values, $message = '')
 * @method static void allLength($values, $length, $message = '')
 * @method static void allMinLength($values, $min, $message = '')
 * @method static void allMaxLength($values, $max, $message = '')
 * @method static void allLengthBetween($values, $min, $max, $message = '')
 * @method static void allFileExists($values, $message = '')
 * @method static void allFile($values, $message = '')
 * @method static void allDirectory($values, $message = '')
 * @method static void allReadable($values, $message = '')
 * @method static void allWritable($values, $message = '')
 * @method static void allClassExists($values, $message = '')
 * @method static void allSubclassOf($values, $class, $message = '')
 * @method static void allImplementsInterface($values, $interface, $message = '')
 * @method static void allPropertyExists($values, $property, $message = '')
 * @method static void allPropertyNotExists($values, $property, $message = '')
 * @method static void allMethodExists($values, $method, $message = '')
 * @method static void allMethodNotExists($values, $method, $message = '')
 * @method static void allKeyExists($values, $key, $message = '')
 * @method static void allKeyNotExists($values, $key, $message = '')
 * @method static void allCount($values, $key, $message = '')
 * @method static void allUuid($values, $message = '')
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class Assert
{
    public static function string($value, $message = '')
    {
        if (!is_string($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a string. Got: %s',
                static::typeToString($value)
            ));
        }
    }

    public static function stringNotEmpty($value, $message = '')
    {
        static::string($value, $message);
        static::notEmpty($value, $message);
    }

    public static function integer($value, $message = '')
    {
        if (!is_int($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected an integer. Got: %s',
                static::typeToString($value)
            ));
        }
    }

    public static function integerish($value, $message = '')
    {
        if (!is_numeric($value) || $value != (int) $value) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected an integerish value. Got: %s',
                static::typeToString($value)
            ));
        }
    }

    public static function float($value, $message = '')
    {
        if (!is_float($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a float. Got: %s',
                static::typeToString($value)
            ));
        }
    }

    public static function numeric($value, $message = '')
    {
        if (!is_numeric($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a numeric. Got: %s',
                static::typeToString($value)
            ));
        }
    }

    public static function boolean($value, $message = '')
    {
        if (!is_bool($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a boolean. Got: %s',
                static::typeToString($value)
            ));
        }
    }

    public static function scalar($value, $message = '')
    {
        if (!is_scalar($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a scalar. Got: %s',
                static::typeToString($value)
            ));
        }
    }

    public static function object($value, $message = '')
    {
        if (!is_object($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected an object. Got: %s',
                static::typeToString($value)
            ));
        }
    }

    public static function resource($value, $type = null, $message = '')
    {
        if (!is_resource($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a resource. Got: %s',
                static::typeToString($value)
            ));
        }

        if ($type && $type !== get_resource_type($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a resource of type %2$s. Got: %s',
                static::typeToString($value),
                $type
            ));
        }
    }

    public static function isCallable($value, $message = '')
    {
        if (!is_callable($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a callable. Got: %s',
                static::typeToString($value)
            ));
        }
    }

    public static function isArray($value, $message = '')
    {
        if (!is_array($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected an array. Got: %s',
                static::typeToString($value)
            ));
        }
    }

    public static function isTraversable($value, $message = '')
    {
        if (!is_array($value) && !($value instanceof Traversable)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a traversable. Got: %s',
                static::typeToString($value)
            ));
        }
    }

    public static function isInstanceOf($value, $class, $message = '')
    {
        if (!($value instanceof $class)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected an instance of %2$s. Got: %s',
                static::typeToString($value),
                $class
            ));
        }
    }

    public static function notInstanceOf($value, $class, $message = '')
    {
        if ($value instanceof $class) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected an instance other than %2$s. Got: %s',
                static::typeToString($value),
                $class
            ));
        }
    }

    public static function isEmpty($value, $message = '')
    {
        if (!empty($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected an empty value. Got: %s',
                static::valueToString($value)
            ));
        }
    }

    public static function notEmpty($value, $message = '')
    {
        if (empty($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a non-empty value. Got: %s',
                static::valueToString($value)
            ));
        }
    }

    public static function null($value, $message = '')
    {
        if (null !== $value) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected null. Got: %s',
                static::valueToString($value)
            ));
        }
    }

    public static function notNull($value, $message = '')
    {
        if (null === $value) {
            static::reportInvalidArgument(
                $message ?: 'Expected a value other than null.'
            );
        }
    }

    public static function true($value, $message = '')
    {
        if (true !== $value) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value to be true. Got: %s',
                static::valueToString($value)
            ));
        }
    }

    public static function false($value, $message = '')
    {
        if (false !== $value) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value to be false. Got: %s',
                static::valueToString($value)
            ));
        }
    }

    public static function eq($value, $value2, $message = '')
    {
        if ($value2 != $value) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value equal to %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($value2)
            ));
        }
    }

    public static function notEq($value, $value2, $message = '')
    {
        if ($value2 == $value) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a different value than %s.',
                static::valueToString($value2)
            ));
        }
    }

    public static function same($value, $value2, $message = '')
    {
        if ($value2 !== $value) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value identical to %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($value2)
            ));
        }
    }

    public static function notSame($value, $value2, $message = '')
    {
        if ($value2 === $value) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value not identical to %s.',
                static::valueToString($value2)
            ));
        }
    }

    public static function greaterThan($value, $limit, $message = '')
    {
        if ($value <= $limit) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value greater than %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($limit)
            ));
        }
    }

    public static function greaterThanEq($value, $limit, $message = '')
    {
        if ($value < $limit) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value greater than or equal to %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($limit)
            ));
        }
    }

    public static function lessThan($value, $limit, $message = '')
    {
        if ($value >= $limit) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value less than %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($limit)
            ));
        }
    }

    public static function lessThanEq($value, $limit, $message = '')
    {
        if ($value > $limit) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value less than or equal to %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($limit)
            ));
        }
    }

    public static function range($value, $min, $max, $message = '')
    {
        if ($value < $min || $value > $max) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value between %2$s and %3$s. Got: %s',
                static::valueToString($value),
                static::valueToString($min),
                static::valueToString($max)
            ));
        }
    }

    public static function oneOf($value, array $values, $message = '')
    {
        if (!in_array($value, $values, true)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected one of: %2$s. Got: %s',
                static::valueToString($value),
                implode(', ', array_map(array('static', 'valueToString'), $values))
            ));
        }
    }

    public static function contains($value, $subString, $message = '')
    {
        if (false === strpos($value, $subString)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value to contain %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($subString)
            ));
        }
    }

    public static function startsWith($value, $prefix, $message = '')
    {
        if (0 !== strpos($value, $prefix)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value to start with %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($prefix)
            ));
        }
    }

    public static function startsWithLetter($value, $message = '')
    {
        $valid = isset($value[0]);

        if ($valid) {
            $locale = setlocale(LC_CTYPE, 0);
            setlocale(LC_CTYPE, 'C');
            $valid = ctype_alpha($value[0]);
            setlocale(LC_CTYPE, $locale);
        }

        if (!$valid) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value to start with a letter. Got: %s',
                static::valueToString($value)
            ));
        }
    }

    public static function endsWith($value, $suffix, $message = '')
    {
        if ($suffix !== substr($value, -static::strlen($suffix))) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value to end with %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($suffix)
            ));
        }
    }

    public static function regex($value, $pattern, $message = '')
    {
        if (!preg_match($pattern, $value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'The value %s does not match the expected pattern.',
                static::valueToString($value)
            ));
        }
    }

    public static function alpha($value, $message = '')
    {
        $locale = setlocale(LC_CTYPE, 0);
        setlocale(LC_CTYPE, 'C');
        $valid = !ctype_alpha($value);
        setlocale(LC_CTYPE, $locale);

        if ($valid) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value to contain only letters. Got: %s',
                static::valueToString($value)
            ));
        }
    }

    public static function digits($value, $message = '')
    {
        $locale = setlocale(LC_CTYPE, 0);
        setlocale(LC_CTYPE, 'C');
        $valid = !ctype_digit($value);
        setlocale(LC_CTYPE, $locale);

        if ($valid) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value to contain digits only. Got: %s',
                static::valueToString($value)
            ));
        }
    }

    public static function alnum($value, $message = '')
    {
        $locale = setlocale(LC_CTYPE, 0);
        setlocale(LC_CTYPE, 'C');
        $valid = !ctype_alnum($value);
        setlocale(LC_CTYPE, $locale);

        if ($valid) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value to contain letters and digits only. Got: %s',
                static::valueToString($value)
            ));
        }
    }

    public static function lower($value, $message = '')
    {
        $locale = setlocale(LC_CTYPE, 0);
        setlocale(LC_CTYPE, 'C');
        $valid = !ctype_lower($value);
        setlocale(LC_CTYPE, $locale);

        if ($valid) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value to contain lowercase characters only. Got: %s',
                static::valueToString($value)
            ));
        }
    }

    public static function upper($value, $message = '')
    {
        $locale = setlocale(LC_CTYPE, 0);
        setlocale(LC_CTYPE, 'C');
        $valid = !ctype_upper($value);
        setlocale(LC_CTYPE, $locale);

        if ($valid) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value to contain uppercase characters only. Got: %s',
                static::valueToString($value)
            ));
        }
    }

    public static function length($value, $length, $message = '')
    {
        if ($length !== static::strlen($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value to contain %2$s characters. Got: %s',
                static::valueToString($value),
                $length
            ));
        }
    }

    public static function minLength($value, $min, $message = '')
    {
        if (static::strlen($value) < $min) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value to contain at least %2$s characters. Got: %s',
                static::valueToString($value),
                $min
            ));
        }
    }

    public static function maxLength($value, $max, $message = '')
    {
        if (static::strlen($value) > $max) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value to contain at most %2$s characters. Got: %s',
                static::valueToString($value),
                $max
            ));
        }
    }

    public static function lengthBetween($value, $min, $max, $message = '')
    {
        $length = static::strlen($value);

        if ($length < $min || $length > $max) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s',
                static::valueToString($value),
                $min,
                $max
            ));
        }
    }

    public static function fileExists($value, $message = '')
    {
        static::string($value);

        if (!file_exists($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'The file %s does not exist.',
                static::valueToString($value)
            ));
        }
    }

    public static function file($value, $message = '')
    {
        static::fileExists($value, $message);

        if (!is_file($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'The path %s is not a file.',
                static::valueToString($value)
            ));
        }
    }

    public static function directory($value, $message = '')
    {
        static::fileExists($value, $message);

        if (!is_dir($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'The path %s is no directory.',
                static::valueToString($value)
            ));
        }
    }

    public static function readable($value, $message = '')
    {
        if (!is_readable($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'The path %s is not readable.',
                static::valueToString($value)
            ));
        }
    }

    public static function writable($value, $message = '')
    {
        if (!is_writable($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'The path %s is not writable.',
                static::valueToString($value)
            ));
        }
    }

    public static function classExists($value, $message = '')
    {
        if (!class_exists($value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected an existing class name. Got: %s',
                static::valueToString($value)
            ));
        }
    }

    public static function subclassOf($value, $class, $message = '')
    {
        if (!is_subclass_of($value, $class)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected a sub-class of %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($class)
            ));
        }
    }

    public static function implementsInterface($value, $interface, $message = '')
    {
        if (!in_array($interface, class_implements($value))) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected an implementation of %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($interface)
            ));
        }
    }

    public static function propertyExists($classOrObject, $property, $message = '')
    {
        if (!property_exists($classOrObject, $property)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected the property %s to exist.',
                static::valueToString($property)
            ));
        }
    }

    public static function propertyNotExists($classOrObject, $property, $message = '')
    {
        if (property_exists($classOrObject, $property)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected the property %s to not exist.',
                static::valueToString($property)
            ));
        }
    }

    public static function methodExists($classOrObject, $method, $message = '')
    {
        if (!method_exists($classOrObject, $method)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected the method %s to exist.',
                static::valueToString($method)
            ));
        }
    }

    public static function methodNotExists($classOrObject, $method, $message = '')
    {
        if (method_exists($classOrObject, $method)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected the method %s to not exist.',
                static::valueToString($method)
            ));
        }
    }

    public static function keyExists($array, $key, $message = '')
    {
        if (!array_key_exists($key, $array)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected the key %s to exist.',
                static::valueToString($key)
            ));
        }
    }

    public static function keyNotExists($array, $key, $message = '')
    {
        if (array_key_exists($key, $array)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Expected the key %s to not exist.',
                static::valueToString($key)
            ));
        }
    }

    public static function count($array, $number, $message = '')
    {
        static::eq(
            count($array),
            $number,
            $message ?: sprintf('Expected an array to contain %d elements. Got: %d.', $number, count($array))
        );
    }

    public static function uuid($value, $message = '')
    {
        $value = str_replace(array('urn:', 'uuid:', '{', '}'), '', $value);

        // The nil UUID is special form of UUID that is specified to have all
        // 128 bits set to zero.
        if ('00000000-0000-0000-0000-000000000000' === $value) {
            return;
        }

        if (!preg_match('/^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/', $value)) {
            static::reportInvalidArgument(sprintf(
                $message ?: 'Value %s is not a valid UUID.',
                static::valueToString($value)
            ));
        }
    }

    public static function throws(Closure $expression, $class = 'Exception', $message = '')
    {
        static::string($class);

        $actual = 'none';
        try {
            $expression();
        } catch (Exception $e) {
            $actual = get_class($e);
            if ($e instanceof $class) {
                return;
            }
        } catch (Throwable $e) {
            $actual = get_class($e);
            if ($e instanceof $class) {
                return;
            }
        }

        static::reportInvalidArgument($message ?: sprintf(
            'Expected to throw "%s", got "%s"',
            $class,
            $actual
        ));
    }

    public static function __callStatic($name, $arguments)
    {
        if ('nullOr' === substr($name, 0, 6)) {
            if (null !== $arguments[0]) {
                $method = lcfirst(substr($name, 6));
                call_user_func_array(array('static', $method), $arguments);
            }

            return;
        }

        if ('all' === substr($name, 0, 3)) {
            static::isTraversable($arguments[0]);

            $method = lcfirst(substr($name, 3));
            $args = $arguments;

            foreach ($arguments[0] as $entry) {
                $args[0] = $entry;

                call_user_func_array(array('static', $method), $args);
            }

            return;
        }

        throw new BadMethodCallException('No such method: '.$name);
    }

    protected static function valueToString($value)
    {
        if (null === $value) {
            return 'null';
        }

        if (true === $value) {
            return 'true';
        }

        if (false === $value) {
            return 'false';
        }

        if (is_array($value)) {
            return 'array';
        }

        if (is_object($value)) {
            return get_class($value);
        }

        if (is_resource($value)) {
            return 'resource';
        }

        if (is_string($value)) {
            return '"'.$value.'"';
        }

        return (string) $value;
    }

    protected static function typeToString($value)
    {
        return is_object($value) ? get_class($value) : gettype($value);
    }

    protected static function strlen($value)
    {
        if (!function_exists('mb_detect_encoding')) {
            return strlen($value);
        }

        if (false === $encoding = mb_detect_encoding($value)) {
            return strlen($value);
        }

        return mb_strwidth($value, $encoding);
    }

    protected static function reportInvalidArgument($message)
    {
        throw new InvalidArgumentException($message);
    }

    private function __construct()
    {
    }
}
<?php

namespace OpenTok;

use OpenTok\Util\Client;
use OpenTok\Util\Validators;

use OpenTok\Exception\InvalidArgumentException;
use OpenTok\Exception\ArchiveUnexpectedValueException;

/**
* Represents an archive of an OpenTok session.
*
* @property int $createdAt
* The time at which the archive was created, in milliseconds since the UNIX epoch.
*
* @property string $duration
* The duration of the archive, in milliseconds.
*
* @property bool $hasVideo
* Whether the archive has a video track (<code>true</code>) or not (<code>false</code>).
*
* @property bool $hasAudio
* Whether the archive has an audio track (<code>true</code>) or not (<code>false</code>).
*
* @property string $id
* The archive ID.
*
* @property string $name
* The name of the archive. If no name was provided when the archive was created, this is set
* to null.
*
* @property string $outputMode
* The name of the archive. If no name was provided when the archive was created, this is set
* to null.
*
* @property string $partnerId
* The API key associated with the archive.
*
* @property string $reason
* For archives with the status "stopped" or "failed", this string describes the reason
* the archive stopped (such as "maximum duration exceeded") or failed.
*
* @property string $sessionId
* The session ID of the OpenTok session associated with this archive.
*
* @property string $size
* The size of the MP4 file. For archives that have not been generated, this value is set to 0.
*
* @property string $status
* The status of the archive, which can be one of the following:
*
* <ul>
*   <li> "available" -- The archive is available for download from the OpenTok cloud.</li>
*   <li> "expired" -- The archive is no longer available for download from the OpenTok
*         cloud.</li>
*   <li> "failed" -- The archive recording failed.</li>
*   <li> "paused" -- The archive is in progress and no clients are publishing streams to
*        the session. When an archive is in progress and any client publishes a stream,
*        the status is "started". When an archive is "paused", nothing is recorded. When
*        a client starts publishing a stream, the recording starts (or resumes). If all clients
*        disconnect from a session that is being archived, the status changes to "paused", and
*        after 60 seconds the archive recording stops (and the status changes to "stopped").</li>
*   <li> "started" -- The archive started and is in the process of being recorded.</li>
*   <li> "stopped" -- The archive stopped recording.</li>
*   <li> "uploaded" -- The archive is available for download from the the upload target
*        Amazon S3 bucket or Windows Azure container you set up for your
*        <a href="https://tokbox.com/account">OpenTok project</a>.</li>
* </ul>
*
* @property string $url
* The download URL of the available MP4 file. This is only set for an archive with the status set to
* "available"; for other archives, (including archives with the status "uploaded") this property is
* set to null. The download URL is obfuscated, and the file is only available from the URL for
* 10 minutes. To generate a new URL, call the Archive.listArchives() or OpenTok.getArchive() method.
*/
class Archive {
    // NOTE: after PHP 5.3.0 support is dropped, the class can implement JsonSerializable

    /** @internal */
    private $data;
    /** @internal */
    private $isDeleted;
    /** @internal */
    private $client;

    /** @internal */
    public function __construct($archiveData, $options = array())
    {
        // unpack optional arguments (merging with default values) into named variables
        $defaults = array(
            'apiKey' => null,
            'apiSecret' => null,
            'apiUrl' => 'https://api.opentok.com',
            'client' => null
        );
        $options = array_merge($defaults, array_intersect_key($options, $defaults));
        list($apiKey, $apiSecret, $apiUrl, $client) = array_values($options);

        // validate params
        Validators::validateArchiveData($archiveData);
        Validators::validateClient($client);

        $this->data = $archiveData;

        $this->client = isset($client) ? $client : new Client();
        if (!$this->client->isConfigured()) {
            Validators::validateApiKey($apiKey);
            Validators::validateApiSecret($apiSecret);
            Validators::validateApiUrl($apiUrl);

            $this->client->configure($apiKey, $apiSecret, $apiUrl);
        }
    }

    /** @internal */
    public function __get($name)
    {
        if ($this->isDeleted) {
            // TODO: throw an logic error about not being able to stop an archive thats deleted
        }
        switch($name) {
            case 'createdAt':
            case 'duration':
            case 'id':
            case 'name':
            case 'partnerId':
            case 'reason':
            case 'sessionId':
            case 'size':
            case 'status':
            case 'url':
            case 'hasVideo':
            case 'hasAudio':
            case 'outputMode':
                return $this->data[$name];
                break;
            default:
                return null;
        }
    }

    /**
     * Stops the OpenTok archive, if it is being recorded.
     * <p>
     * Archives automatically stop recording after 120 minutes or when all clients have
     * disconnected from the session being archived.
     *
     * @throws Exception\ArchiveException The archive is not being recorded.
     */
    public function stop()
    {
        if ($this->isDeleted) {
            // TODO: throw an logic error about not being able to stop an archive thats deleted
        }

        $archiveData = $this->client->stopArchive($this->data['id']);

        try {
            Validators::validateArchiveData($archiveData);
        } catch (InvalidArgumentException $e) {
            throw new ArchiveUnexpectedValueException('The archive JSON returned after stopping was not valid', null, $e);
        }

        $this->data = $archiveData;
        return $this;
    }

    /**
     * Deletes an OpenTok archive.
     * <p>
     * You can only delete an archive which has a status of "available", "uploaded", or "deleted".
     * Deleting an archive removes its record from the list of archives. For an "available" archive,
     * it also removes the archive file, making it unavailable for download. For a "deleted"
     * archive, the archive remains deleted.
     *
     * @throws Exception\ArchiveException There archive status is not "available", "updated",
     * or "deleted".
     */
    public function delete()
    {
        if ($this->isDeleted) {
            // TODO: throw an logic error about not being able to stop an archive thats deleted
        }

        if ($this->client->deleteArchive($this->data['id'])) {
            $this->data = array();
            $this->isDeleted = true;
            return true;
        }
        return false;
    }

    /**
     * Returns a JSON representation of this Archive object.
     */
    public function toJson()
    {
        return json_encode($this->jsonSerialize());
    }

    /**
     * Returns an associative array representation of this Archive object.
     * @deprecated 3.0.0 A more standard name for this method is supplied by JsonSerializable
     * @see Archive::jsonSerialize() for a method with the same behavior
     */
    public function toArray()
    {
        return $this->data;
    }

    public function jsonSerialize()
    {
        return $this->data;
    }
}

/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok;

use OpenTok\Util\Client;
use OpenTok\Util\Validators;

// TODO: may want to implement the ArrayAccess interface in the future
// TODO: what does implementing JsonSerializable gain for us?
/**
* A class for accessing an array of Archive objects.
*/
class ArchiveList {

    /**
    * @internal
    */
    private $data;
    /**
    * @internal
    */
    private $apiKey;
    /**
    * @internal
    */
    private $apiSecret;
    /**
    * @internal
    */
    private $client;
    /**
    * @internal
    */
    private $items;

    /**
    * @internal
    */
    public function __construct($archiveListData, $options = array())
    {
        // unpack optional arguments (merging with default values) into named variables
        $defaults = array(
            'apiKey' => null,
            'apiSecret' => null,
            'apiUrl' => 'https://api.opentok.com',
            'client' => null
        );
        $options = array_merge($defaults, array_intersect_key($options, $defaults));
        list($apiKey, $apiSecret, $apiUrl, $client) = array_values($options);

        // validate params
        Validators::validateArchiveListData($archiveListData);
        Validators::validateClient($client);

        $this->data = $archiveListData;

        $this->client = isset($client) ? $client : new Client();
        if (!$this->client->isConfigured()) {
            Validators::validateApiKey($apiKey);
            Validators::validateApiSecret($apiSecret);
            Validators::validateApiUrl($apiUrl);

            $this->client->configure($apiKey, $apiSecret, $apiUrl);
        }
    }

    /**
     * Returns the number of total archives for the API key.
     */
    public function totalCount()
    {
        return $this->data['count'];
    }

    /**
     * Returns an array of Archive objects.
     */
    public function getItems()
    {
        if (!$this->items) {
            $items = array();
            foreach($this->data['items'] as $archiveData) {
                $items[] = new Archive($archiveData, array( 'client' => $this->client ));
            }
            $this->items = $items;
        }
        return $this->items;
    }
}

/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok;

use OpenTok\Util\BasicEnum;

/**
 * Defines values for the archiveMode parameter of the \OpenTok\OpenTok->createSession() method
 * and the return value for the \OpenTok\Session->getArchiveMode() method.
 *
 * See <a href="OpenTok.OpenTok.html#method_createSession">OpenTok->createSession()</a>
 * and <a href="OpenTok.Archive.html#method_getArchiveMode">Session->getArchiveMode()</a>.
 */
abstract class ArchiveMode extends BasicEnum {
    /**
     * The session is not archived automatically. To archive the session, you can call the
     * \OpenTok\OpenTok->startArchive() method.
     */
    const MANUAL = 'manual';
    /**
     * The session is archived automatically (as soon as there are clients publishing streams
     * to the session).
     */
    const ALWAYS = 'always';
}

/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok;

use OpenTok\Exception\BroadcastDomainException;
use OpenTok\Exception\BroadcastUnexpectedValueException;
use OpenTok\Exception\InvalidArgumentException;
use OpenTok\Util\Client;
use OpenTok\Util\Validators;

class Broadcast {
    // NOTE: after PHP 5.3.0 support is dropped, the class can implement JsonSerializable

    private $data;
    private $isStopped = false;
    private $client;

    public function __construct($broadcastData, $options = array()) {
        // unpack optional arguments (merging with default values) into named variables
        $defaults = array(
            'apiKey' => null,
            'apiSecret' => null,
            'apiUrl' => 'https://api.opentok.com',
            'client' => null,
            'isStopped' => false
        );
        $options = array_merge($defaults, array_intersect_key($options, $defaults));
        list($apiKey, $apiSecret, $apiUrl, $client, $isStopped) = array_values($options);

        // validate params
        Validators::validateBroadcastData($broadcastData);
        Validators::validateClient($client);

        $this->data = $broadcastData;

        $this->isStopped = $isStopped;

        $this->client = isset($client) ? $client : new Client();
        if (!$this->client->isConfigured()) {
            Validators::validateApiKey($apiKey);
            Validators::validateApiSecret($apiSecret);
            Validators::validateApiUrl($apiUrl);

            $this->client->configure($apiKey, $apiSecret, $apiUrl);
        }
    }

    public function __get($name)
    {
        switch ($name) {
            case 'createdAt':
            case 'updatedAt':
            case 'id':
            case 'partnerId':
            case 'sessionId':
            case 'broadcastUrls':
                return $this->data[$name];
                break;
            case 'hlsUrl':
                return $this->data['broadcastUrls']['hls'];
                break;
            case 'isStopped':
                return $this->isStopped;
                break;
            default:
                return null;
        }
    }

    public function stop()
    {
        if ($this->isStopped) {
            throw new BroadcastDomainException(
                'You cannot stop a broadcast which is already stopped.'
            );
        }

        $broadcastData = $this->client->stopBroadcast($this->data['id']);

        try {
            Validators::validateBroadcastData($broadcastData);
        } catch (InvalidArgumentException $e) {
            throw new BroadcastUnexpectedValueException('The broadcast JSON returned after stopping was not valid', null, $e);
        }

        $this->data = $broadcastData;
        return $this;
    }

    // TODO: not yet implemented by the platform
    // public function getLayout()
    // {
    //     $layoutData = $this->client->getLayout($this->id, 'broadcast');
    //     return Layout::fromData($layoutData);
    // }

    public function updateLayout($layout)
    {
        Validators::validateLayout($layout);

        // TODO: platform implementation did not meet API review spec
        // $layoutData = $this->client->updateLayout($this->id, $layout, 'broadcast');
        // return Layout::fromData($layoutData);

        $this->client->updateLayout($this->id, $layout, 'broadcast');
    }

    public function jsonSerialize()
    {
        return $this->data;
    }
}
<?php

namespace OpenTok\Exception;

/**
 * Defines the exception thrown when you use an invalid API or secret and call an archiving method.
 */
class ArchiveAuthenticationException extends \OpenTok\Exception\AuthenticationException implements \OpenTok\Exception\ArchiveException
{
}
/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok\Exception;

// TODO: this kind of exception has a detailed message from the HTTP response, use it.
/**
* Defines an exception thrown when a call to an archiving method results in an error response from
* the server.
*/
class ArchiveDomainException extends \OpenTok\Exception\DomainException implements \OpenTok\Exception\ArchiveException
{
}
/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok\Exception;

/**
* The interface used by all exceptions resulting from calls to the archiving API.
*/
interface ArchiveException
{
}
/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok\Exception;

/**
* Defines an exception thrown when a call to an archiving method results in an error due to an
* unexpected value.
*/
class ArchiveUnexpectedValueException extends \OpenTok\Exception\UnexpectedValueException implements \OpenTok\Exception\ArchiveException
{
}
/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok\Exception;

// TODO: this kind of exception is more meaningful if we pass in the apiKey and apiSecret
/**
 * Defines the exception thrown when you use an invalid API or secret.
 */
class AuthenticationException extends \OpenTok\Exception\DomainException implements \OpenTok\Exception\Exception
{
    public function __construct($apiKey, $apiSecret, $code = 0, $previous)
    {
        $message = 'The OpenTok API credentials were rejected. apiKey='.$apiKey.', apiSecret='.$apiSecret;
         parent::__construct($message, $code, $previous);
    }
}
/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok\Exception;

/**
 * Defines the exception thrown when you use an invalid API or secret and call a broadcast method.
 */
class BroadcastAuthenticationException extends \OpenTok\Exception\AuthenticationException implements \OpenTok\Exception\BroadcastException
{
}
/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok\Exception;

// TODO: this kind of exception has a detailed message from the HTTP response, use it.
/**
* Defines an exception thrown when a call to a broadcast method results in an error response from
* the server.
*/
class BroadcastDomainException extends \OpenTok\Exception\DomainException implements \OpenTok\Exception\BroadcastException
{
}
/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok\Exception;

/**
* The interface used by all exceptions resulting from calls to the broadcast API.
*/
interface BroadcastException
{
}
/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok\Exception;

/**
* Defines an exception thrown when a call to a broadcast method results in an error due to an
* unexpected value.
*/
class BroadcastUnexpectedValueException extends \OpenTok\Exception\UnexpectedValueException implements \OpenTok\Exception\BroadcastException
{
}
/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok\Exception;

/**
* Defines an exception thrown when an API call results in an error response from
* the server.
*/
class DomainException extends \DomainException implements \OpenTok\Exception\Exception
{
}
/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok\Exception;

/**
* The interface used by all exceptions in the OpenTok PHP API.
*/
interface Exception 
{
}
/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok\Exception;

/**
* Defines an exception thrown when you pass an invalid argument into a method.
*/
class InvalidArgumentException extends \InvalidArgumentException implements \OpenTok\Exception\Exception
{
}
/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok\Exception;

/**
* Defines an exception thrown in result of an unexpected value.
*/
class UnexpectedValueException extends \UnexpectedValueException implements \OpenTok\Exception\Exception
{
}
/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok;

use OpenTok\Util\Validators;

class Layout {
    // NOTE: after PHP 5.3.0 support is dropped, the class can implement JsonSerializable

    private static $bestFit = null;
    private static $pip = null;
    private static $verticalPresentation = null;
    private static $horizontalPresentaton = null;

    public static function getBestFit()
    {
        if (is_null(self::$bestFit)) {
            self::$bestFit = new Layout('bestFit');
        }
        return self::$bestFit;
    }

    public static function getPIP()
    {
        if (is_null(self::$pip)) {
            self::$pip = new Layout('pip');
        }
        return self::$pip;
    }

    public static function getVerticalPresentation()
    {
        if (is_null(self::$verticalPresentation)) {
            self::$verticalPresentation = new Layout('verticalPresentation');
        }
        return self::$verticalPresentation;
    }

    public static function getHorizontalPresentation()
    {
        if (is_null(self::$horizontalPresentaton)) {
            self::$horizontalPresentaton = new Layout('horizontalPresentaton');
        }
        return self::$horizontalPresentaton;
    }

    public static function createCustom($options)
    {
        // unpack optional arguments (merging with default values) into named variables
        // NOTE: the default value of stylesheet=null will not pass validation, this essentially
        //       means that stylesheet is not optional. its still purposely left as part of the
        //       $options argument so that it can become truly optional in the future.
        $defaults = array('stylesheet' => null);
        $options = array_merge($defaults, array_intersect_key($options, $defaults));
        list($stylesheet) = array_values($options);

        // validate arguments
        Validators::validateLayoutStylesheet($stylesheet);

        return new Layout('custom', $stylesheet);
    }


    public static function fromData($layoutData)
    {
        if (array_key_exists('stylesheet', $layoutData)) {
            return new Layout($layoutData['type'], $layoutData['stylesheet']);
        } else {
            return new Layout($layoutData['type']);
        }
    }

    private $type;
    private $stylesheet;

    private function __construct($type, $stylesheet = null)
    {
        $this->type = $type;
        $this->stylesheet = $stylesheet;
    }

    public function jsonSerialize() {
        $data = array(
            'type' => $this->type
        );

        // omit 'stylesheet' property unless it is explicitly defined
        if (isset($this->stylesheet)) {
            $data['stylesheet'] = $this->stylesheet;
        }
        return $data;
    }

    public function toJson()
    {
        return json_encode($this->jsonSerialize());
    }
}
<?php

namespace OpenTok;

use OpenTok\Util\BasicEnum;

/**
 * Defines values for the mediaMode parameter of the \OpenTok\OpenTok->createSession()
 * method.
 */
abstract class MediaMode extends BasicEnum {
    /**
    *   The session will send streams using the OpenTok Media Router.
    */
    const ROUTED = 'disabled';
    /**
    *   The session will attempt send streams directly between clients. If clients cannot connect
    *   due to firewall restrictions, the session uses the OpenTok TURN server to relay streams.
    */
    const RELAYED = 'enabled';
}

/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok;

use OpenTok\Session;
use OpenTok\Archive;
use OpenTok\Broadcast;
use OpenTok\Layout;
use OpenTok\Role;
use OpenTok\MediaMode;
use OpenTok\ArchiveMode;
use OpenTok\OutputMode;
use OpenTok\Util\Client;
use OpenTok\Util\Validators;

use OpenTok\Exception\UnexpectedValueException;
use OpenTok\Exception\InvalidArgumentException;

/**
* Contains methods for creating OpenTok sessions, generating tokens, and working with archives.
* <p>
* To create a new OpenTok object, call the OpenTok() constructor with your OpenTok API key
* and the API secret for your <a href="https://tokbox.com/account">TokBox account</a>. Do not
* publicly share your API secret. You will use it with the OpenTok() constructor (only on your web
* server) to create OpenTok sessions.
* <p>
* Be sure to include the entire OpenTok server SDK on your web server.
*/
class OpenTok {

    /** @internal */
    private $apiKey;
    /** @internal */
    private $apiSecret;
    /** @internal */
    private $client;

    /** @internal */
    public function __construct($apiKey, $apiSecret, $options = array())
    {
        // unpack optional arguments (merging with default values) into named variables
        $defaults = array('apiUrl' => 'https://api.opentok.com', 'client' => null);
        $options = array_merge($defaults, array_intersect_key($options, $defaults));
        list($apiUrl, $client) = array_values($options);

        // validate arguments
        Validators::validateApiKey($apiKey);
        Validators::validateApiSecret($apiSecret);
        Validators::validateApiUrl($apiUrl);
        Validators::validateClient($client);

        $this->client = isset($client) ? $client : new Client();
        $this->client->configure($apiKey, $apiSecret, $apiUrl);
        $this->apiKey = $apiKey;
        $this->apiSecret = $apiSecret;
    }

    /**
     * Creates a token for connecting to an OpenTok session. In order to authenticate a user
     * connecting to an OpenTok session, the client passes a token when connecting to the session.
     * <p>
     * For testing, you generate tokens or by logging in to your
     * <a href="https://tokbox.com/account">TokBox account</a>.
     *
     * @param string $sessionId The session ID corresponding to the session to which the user
     * will connect.
     *
     * @param array $options This array defines options for the token. This array includes the
     * following keys, all of which are optional:
     *
     * <ul>
     *
     *    <li><code>'role'</code> (string) &mdash; One of the constants defined in the RoleConstants
     *    class. The default role is publisher</li>
     *
     *    <li><code>'expireTime'</code> (int) &mdash; The timestamp for when the token expires,
     *    in milliseconds since the Unix epoch. The default expiration time is 24 hours
     *    after the token creation time. The maximum expiration time is 30 days after the
     *    token creation time.</li>
     *
     *    <li><code>'data'</code> (string) &mdash; A string containing connection metadata
     *    describing the end-user. For example, you can pass the user ID, name, or other data
     *    describing the end-user. The length of the string is limited to 1000 characters.
     *    This data cannot be updated once it is set.</li>
     *
     * </ul>
     *
     * @return string The token string.
     */
    public function generateToken($sessionId, $options = array())
    {
        // unpack optional arguments (merging with default values) into named variables
        $defaults = array(
            'role' => Role::PUBLISHER,
            'expireTime' => null,
            'data' => null
        );
        $options = array_merge($defaults, array_intersect_key($options, $defaults));
        list($role, $expireTime, $data) = array_values($options);

        // additional token data
        $createTime = time();
        $nonce = microtime(true) . mt_rand();

        // validate arguments
        Validators::validateSessionIdBelongsToKey($sessionId, $this->apiKey);
        Validators::validateRole($role);
        Validators::validateExpireTime($expireTime, $createTime);
        Validators::validateData($data);

        $dataString = "session_id=$sessionId&create_time=$createTime&role=$role&nonce=$nonce" .
            (($expireTime) ? "&expire_time=$expireTime" : '') .
            (($data) ? "&connection_data=" . urlencode($data) : '');
        $sig = $this->_sign_string($dataString, $this->apiSecret);

        return "T1==" . base64_encode("partner_id=$this->apiKey&sig=$sig:$dataString");
    }

    /**
    * Creates a new OpenTok session and returns the session ID, which uniquely identifies
    * the session.
    * <p>
    * For example, when using the OpenTok JavaScript library, use the session ID when calling the
    * <a href="http://tokbox.com/opentok/libraries/client/js/reference/OT.html#initSession">
    * OT.initSession()</a> method (to initialize an OpenTok session).
    * <p>
    * OpenTok sessions do not expire. However, authentication tokens do expire (see the
    * generateToken() method). Also note that sessions cannot explicitly be destroyed.
    * <p>
    * A session ID string can be up to 255 characters long.
    * <p>
    * Calling this method results in an OpenTokException in the event of an error.
    * Check the error message for details.
    * <p>
    * You can also create a session by logging in to your
    * <a href="https://tokbox.com/account">TokBox account</a>.
    *
    * @param array $options (Optional) This array defines options for the session. The array includes
    * the following keys (all of which are optional):
    *
    * <ul>
    *
    *    <li><code>'archiveMode'</code> (ArchiveMode) &mdash; Whether the session is automatically
    *    archived (<code>ArchiveMode::ALWAYS</code>) or not (<code>ArchiveMode::MANUAL</code>).
    *    By default, the setting is <code>ArchiveMode.MANUAL</code>, and you must call the
    *    <code>OpenTok->startArchive()</code> method to start archiving. To archive the session
    *    (either automatically or not), you must set the <code>mediaMode</code> key to
    *    <code>MediaMode::ROUTED</code>.</li>
    *
    *    <li><code>'location'</code> (String) &mdash; An IP address that the OpenTok servers
    *    will use to situate the session in its global network. If you do not set a location hint,
    *    the OpenTok servers will be based on the first client connecting to the session.</li>
    *
    *     <li><code>'mediaMode'</code> (MediaMode) &mdash; Whether the session will transmit
    *     streams using the OpenTok Media Router (<code>MediaMode.ROUTED</code>) or not
    *     (<code>MediaMode.RELAYED</code>). By default, the <code>mediaMode</code> property
    *     is set to <code>MediaMode.RELAYED</code>.
    *
    *     <p>
    *     With the <code>mediaMode</code> parameter set to <code>MediaMode.RELAYED</code>, the
    *     session will attempt to transmit streams directly between clients. If clients cannot
    *     connect due to firewall restrictions, the session uses the OpenTok TURN server to relay
    *     audio-video streams.
    *
    *     <p>
    *     The
    *     <a href="https://tokbox.com/opentok/tutorials/create-session/#media-mode" target="_top">
    *     OpenTok Media Router</a> provides the following benefits:
    *
    *     <ul>
    *       <li>The OpenTok Media Router can decrease bandwidth usage in multiparty sessions.
    *           (When the <code>mediaMode</code> parameter is set to <code>MediaMode.ROUTED</code>,
    *           each client must send a separate audio-video stream to each client subscribing to
    *           it.)</li>
    *       <li>The OpenTok Media Router can improve the quality of the user experience through
    *         recovery</a>. With these features, if a client's connectivity degrades to a degree
    *         that it does not support video for a stream it's subscribing to, the video is dropped
    *         on that client (without affecting other clients), and the client receives audio only.
    *         If the client's connectivity improves, the video returns.</li>
    *       <li>The OpenTok Media Router supports the
    *         <a href="https://tokbox.com/opentok/tutorials/archiving" target="_top">archiving</a>
    *         feature, which lets you record, save, and retrieve OpenTok sessions.</li>
    *     </ul>
    *
    * </ul>
    *
    * @return \OpenTok\Session A Session object representing the new session. Call the
    * <code>getSessionId()</code> method of this object to get the session ID. For example,
    * when using the OpenTok.js library, use this session ID when calling the
    * <code>OT.initSession()</code> method.
    */
    public function createSession($options=array())
    {
        if (array_key_exists('archiveMode', $options) &&
            $options['archiveMode'] != ArchiveMode::MANUAL) {

            if (array_key_exists('mediaMode', $options) &&
                $options['mediaMode'] != MediaMode::ROUTED) {

                throw new InvalidArgumentException('A session must be routed to be archived.');
            } else {
              $options['mediaMode'] = MediaMode::ROUTED;
            }
        }

        // unpack optional arguments (merging with default values) into named variables
        $defaults = array(
            'mediaMode' => MediaMode::RELAYED,
            'archiveMode' => ArchiveMode::MANUAL,
            'location' => null
        );
        $options = array_merge($defaults, array_intersect_key($options, $defaults));
        list($mediaMode, $archiveMode, $location) = array_values($options);

        // validate arguments
        Validators::validateMediaMode($mediaMode);
        Validators::validateArchiveMode($archiveMode);
        Validators::validateLocation($location);

        // make API call
        $sessionXml = $this->client->createSession($options);

        // check response
        $sessionId = $sessionXml->Session->session_id;
        if (!$sessionId) {
            $errorMessage = 'Failed to create a session. Server response: '. (string)$sessionXml;
            throw new UnexpectedValueException($errorMessage);
        }

        return new Session($this, (string)$sessionId, array(
            'location' => $location,
            'mediaMode' => $mediaMode,
            'archiveMode' => $archiveMode
        ));
    }

    /**
     * Starts archiving an OpenTok session.
     * <p>
     * Clients must be actively connected to the OpenTok session for you to successfully start
     * recording an archive.
     * <p>
     * You can only record one archive at a time for a given session. You can only record archives
     * of sessions that use the OpenTok Media Router (sessions with the
     * <a href="http://tokbox.com/opentok/tutorials/create-session/#media-mode">media mode</a>
     * set to routed); you cannot archive sessions with the media mode set to relayed.
     * <p>
     * For more information on archiving, see the
     * <a href="https://tokbox.com/opentok/tutorials/archiving/">OpenTok archiving</a> programming
     * guide.
     *
     * @param String $sessionId The session ID of the OpenTok session to archive.
     * @param array $options (Optional) This array defines options for the archive. The array
     * includes the following keys (all of which are optional):
     *
     * <ul>
     *
     *    <li><code>'name'</code> (String) &mdash; The name of the archive. You can use this name to
     *    identify the archive. It is a property of the Archive object, and it is a property of
     *    archive-related events in the OpenTok client SDKs.</li>
     *
     *    <li><code>'hasVideo'</code> (Boolean) &mdash; Whether the archive will record video
     *    (true, the default) or not (false). If you set both <code>hasAudio</code> and
     *    <code>hasVideo</code> to false, the call to the <code>startArchive()</code> method results
     *    in an error.</li>
     *
     *    <li><code>'hasAudio'</code> (Boolean) &mdash; Whether the archive will record audio
     *    (true, the default) or not (false). If you set both <code>hasAudio</code> and
     *    <code>hasVideo</code> to false, the call to the <code>startArchive()</code> method results
     *    in an error.</li>
     *
     *    <li><code>'outputMode'</code> (OutputMode) &mdash; Whether all streams in the
     *    archive are recorded to a single file (<code>OutputMode::COMPOSED</code>, the default)
     *    or to individual files (<code>OutputMode::INDIVIDUAL</code>).</li>
     *
     * <ul>
     *
     * @return Archive The Archive object, which includes properties defining the archive, including
     * the archive ID.
     */
    public function startArchive($sessionId, $options=array())
    {
        // support for deprecated method signature, remove in v3.0.0 (not before)
        if (!is_array($options)) {
          $options = array('name' => $options);
        }

        // unpack optional arguments (merging with default values) into named variables
        $defaults = array(
            'name' => null,
            'hasVideo' => true,
            'hasAudio' => true,
            'outputMode' => OutputMode::COMPOSED
        );
        $options = array_merge($defaults, array_intersect_key($options, $defaults));
        list($name, $hasVideo, $hasAudio, $outputMode) = array_values($options);

        // validate arguments
        Validators::validateSessionId($sessionId);
        Validators::validateArchiveName($name);
        Validators::validateArchiveHasVideo($hasVideo);
        Validators::validateArchiveHasAudio($hasAudio);
        Validators::validateArchiveOutputMode($outputMode);

        // make API call
        $archiveData = $this->client->startArchive($sessionId, $options);

        return new Archive($archiveData, array( 'client' => $this->client ));
    }

    /**
     * Stops an OpenTok archive that is being recorded.
     * <p>
     * Archives automatically stop recording after 120 minutes or when all clients have disconnected
     * from the session being archived.
     *
     * @param String $archiveId The archive ID of the archive you want to stop recording.
     * @return Archive The Archive object corresponding to the archive being stopped.
     */
    public function stopArchive($archiveId)
    {
        Validators::validateArchiveId($archiveId);

        $archiveData = $this->client->stopArchive($archiveId);
        return new Archive($archiveData, array( 'client' => $this->client ));
    }

    /**
     * Gets an Archive object for the given archive ID.
     *
     * @param String $archiveId The archive ID.
     *
     * @throws ArchiveException There is no archive with the specified ID.
     * @throws InvalidArgumentException The archive ID provided is null or an empty string.
     *
     * @return Archive The Archive object.
     */
    public function getArchive($archiveId)
    {
        Validators::validateArchiveId($archiveId);

        $archiveData = $this->client->getArchive($archiveId);
        return new Archive($archiveData, array( 'client' => $this->client ));
    }

    /**
     * Deletes an OpenTok archive.
     * <p>
     * You can only delete an archive which has a status of "available", "uploaded", or "deleted".
     * Deleting an archive removes its record from the list of archives. For an "available" archive,
     * it also removes the archive file, making it unavailable for download. For a "deleted"
     * archive, the archive remains deleted.
     *
     * @param String $archiveId The archive ID of the archive you want to delete.
     *
     * @return Boolean Returns true on success.
     *
     * @throws ArchiveException There archive status is not "available", "updated",
     * or "deleted".
     */
    public function deleteArchive($archiveId)
    {
        Validators::validateArchiveId($archiveId);

        return $this->client->deleteArchive($archiveId);
    }

    /**
     * Returns an ArchiveList. The <code>items()</code> method of this object returns a list of
     * archives that are completed and in-progress, for your API key.
     *
     * @param integer $offset Optional. The index offset of the first archive. 0 is offset of the
     * most recently started archive. 1 is the offset of the archive that started prior to the most
     * recent archive. If you do not specify an offset, 0 is used.
     * @param integer $count Optional. The number of archives to be returned. The maximum number of
     * archives returned is 1000.
     * @return ArchiveList An ArchiveList object. Call the items() method of the ArchiveList object
     * to return an array of Archive objects.
     */
    public function listArchives($offset=0, $count=null)
    {
        // validate params
        Validators::validateOffsetAndCount($offset, $count);

        $archiveListData = $this->client->listArchives($offset, $count);
        return new ArchiveList($archiveListData, array( 'client' => $this->client ));
    }

    public function forceDisconnect($sessionId, $connectionId)
    {
        Validators::validateSessionId($sessionId);
        Validators::validateConnectionId($connectionId);

        return $this->client->forceDisconnect($sessionId, $connectionId);
    }

    public function startBroadcast($sessionId, $options=array())
    {
        // unpack optional arguments (merging with default values) into named variables
        // NOTE: although the server can be authoritative about the default value of layout, its
        // not preferred to depend on that in the SDK because its then harder to garauntee backwards
        // compatibility
        $defaults = array(
            'layout' => Layout::getBestFit()
        );
        $options = array_merge($defaults, array_intersect_key($options, $defaults));
        list($layout) = array_values($options);

        // validate arguments
        Validators::validateSessionId($sessionId);
        Validators::validateLayout($layout);

        // make API call
        $broadcastData = $this->client->startBroadcast($sessionId, $options);

        return new Broadcast($broadcastData, array( 'client' => $this->client ));
    }

    public function stopBroadcast($broadcastId)
    {
        // validate arguments
        Validators::validateBroadcastId($broadcastId);

        // make API call
        $broadcastData = $this->client->stopBroadcast($broadcastId);
        return new Broadcast($broadcastData, array(
            'client' => $this->client,
            'isStopped' => true
        ));
    }

    public function getBroadcast($broadcastId)
    {
        Validators::validateBroadcastId($broadcastId);

        $broadcastData = $this->client->getBroadcast($broadcastId);
        return new Broadcast($broadcastData, array( 'client' => $this->client ));
    }

    // TODO: not yet implemented by the platform
    // public function getBroadcastLayout($broadcastId)
    // {
    //     Validators::validateBroadcastId($broadcastId);
    //
    //     $layoutData = $this->client->getLayout($broadcastId, 'broadcast');
    //     return Layout::fromData($layoutData);
    // }

    public function updateBroadcastLayout($broadcastId, $layout)
    {
        Validators::validateBroadcastId($broadcastId);
        Validators::validateLayout($layout);

        // TODO: platform implementation does not meet API Review spec
        // $layoutData = $this->client->updateLayout($broadcastId, $layout, 'broadcast');
        // return Layout::fromData($layoutData);

        $this->client->updateLayout($broadcastId, $layout, 'broadcast');
    }

    public function updateStream($sessionId, $streamId, $properties = array())
    {
        // unpack optional arguments (merging with default values) into named variables
        $defaults = array(
            'layoutClassList' => array()
        );
        $properties = array_merge($defaults, array_intersect_key($properties, $defaults));
        list($layoutClassList) = array_values($properties);

        // validate arguments
        Validators::validateSessionId($sessionId);
        Validators::validateStreamId($streamId);
        Validators::validateLayoutClassList($layoutClassList, 'JSON');

        // make API call
        $this->client->updateStream($sessionId, $streamId, $properties);
    }

    /**
     * Initiate an outgoing SIP call
     *
     * @param string $sessionId The OpenTok SessionIdwhere the participant being called
     * will join.
     *
     * @param string $token The token for conecting to an OpenTok session. This is the same
     * as the one created by Session.generateToken.
     *
     * @param string $sipUrl The SIP Uri to be used as destination of the SIP Call initiated from
     * OpenTok to the Third Party SIP Platform.
     * If the SIP Uri contains a transport=tlsheader, the negotiation between TokBox and
     * the SIP Endpoint will be done securely. Note that this will only apply to the negotiation
     * itself, and not to the transmission of audio. If you also need the latter, please see the
     * "secure" property.
     * Example of secure call negotiation:
     * "sip:access@thirparty.com;transport=tls"
     * Example of insecure call negotiation:
     * "sip:access@thirparty.com"
     *
     * @param array $options This array defines options for the token. This array includes the
     * following keys, all of which are optional:
     *
     * <ul>
     *
     *    <li><code>'headers'</code> (array) &mdash; Headers​: Custom Headers to be added to the
     *    SIP INVITE request initiated from OpenTok to the Third Party SIP Platform. All of this
     *    custom headers must start with the "X-" prefix, or a Bad Request (400) will be thrown.</li>
     *
     *    <li><code>'auth'</code> (array) &mdash; Auth​: Username and Password to be used in the SIP
     *    INVITE request for HTTP Digest authentication in case this is required by the Third Party
     *    SIP Platform.
     *
     *     <ul>
     *
     *       <li><code>'username'</code> (string) &mdash; Username: String</li>
     *
     *       <li><code>'password'</code> (string) &mdash; Password: String</li>
     *
     *     </ul>
     *
     *    <li><code>'secure'</code> (int) &mdash; Secure​: Boolean (true or false) flag that indicates
     *    whether the media must be transmitted encrypted or not.</li>
     *
     * </ul>
     *
     * @return SipCall The SipCall, which contains the ids of the Sip connection.
     */
    public function dial($sessionId, $token, $sipUri, $options=array())
    {
        // unpack optional arguments (merging with default values) into named variables
        $defaults = array(
            'auth' => null,
            'headers' => null,
            'secure' => true,
        );
        $options = array_merge($defaults, array_intersect_key($options, $defaults));
        list($headers, $secure) = array_values($options);

        // validate arguments
        Validators::validateSessionIdBelongsToKey($sessionId, $this->apiKey);

        // make API call
        $sipJson = $this->client->dial($sessionId, $token, $sipUri, $options);

        // check response
        $id = $sipJson['id'];
        if (!$id) {
            $errorMessage = 'Failed to initiate a SIP call. Server response: '. (string)$sipJson;
            throw new UnexpectedValueException($errorMessage);
        }

        return new SipCall($sipJson);
    }

    /** @internal */
    private function _sign_string($string, $secret)
    {
        return hash_hmac("sha1", $string, $secret);
    }
}

/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok;

use OpenTok\Util\BasicEnum;

/**
 * Defines values for the outputMode option of the \OpenTok\OpenTok->startArchive() method
 * and for the outputMode property of the Archive class.
 *
 * See <a href="OpenTok.OpenTok.html#method_startArchive">OpenTok->startArchive()</a>
 * and <a href="OpenTok.Archive.html#property_outputMode">Archive.outputMode</a>.
 */
abstract class OutputMode extends BasicEnum {
    /**
     * All streams in the archive are recorded to a single (composed) file.
     */
    const COMPOSED = 'composed';
    /**
     * Each stream in the archive is recorded to its own individual file.
     */
    const INDIVIDUAL = 'individual';
}

/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok;

use OpenTok\Util\BasicEnum;

/**
 * Defines values for the role parameter of the \OpenTok\OpenTok->generateToken()
 * method.
 */
abstract class Role extends BasicEnum {
    /**
    *   A subscriber can only subscribe to streams.
    */
    const SUBSCRIBER = 'subscriber';
    /**
    * A publisher can publish streams, subscribe to streams, and signal. (This is the default
    * value if you do not set a role.)
    */
    const PUBLISHER = 'publisher';
    /**
    * In addition to the privileges granted to a publisher, in clients using the OpenTok.js
    * library, a moderator can call the forceUnpublish() and forceDisconnect() methods of
    * the Session object.
    */
    const MODERATOR = 'moderator';
}

/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok;

use OpenTok\OpenTok;
use OpenTok\MediaMode;
use OpenTok\ArchiveMode;
use OpenTok\Util\Validators;

/**
* Represents an OpenTok session.
* <p>
* Use the \OpenTok\OpenTok->createSession() method to create an OpenTok session. Use the
* getSessionId() method of the Session object to get the session ID.
*/
class Session
{
    /**
     * @internal
     */
    protected $sessionId;
    /**
     * @internal
     */
    protected $location;
    /**
     * @internal
     */
    protected $mediaMode;
    /**
     * @internal
     */
    protected $archiveMode;
    /**
     * @internal
     */
    protected $opentok;

    /**
     * @internal
     */
    function __construct($opentok, $sessionId, $properties = array())
    {
        // unpack arguments
        $defaults = array('mediaMode' => MediaMode::ROUTED, 'archiveMode' => ArchiveMode::MANUAL, 'location' => null);
        $properties = array_merge($defaults, array_intersect_key($properties, $defaults));
        list($mediaMode, $archiveMode, $location) = array_values($properties);

        Validators::validateOpenTok($opentok);
        Validators::validateSessionId($sessionId);
        Validators::validateLocation($location);
        Validators::validateMediaMode($mediaMode);
        Validators::validateArchiveMode($archiveMode);

        $this->opentok = $opentok;
        $this->sessionId = $sessionId;
        $this->location = $location;
        $this->mediaMode = $mediaMode;
        $this->archiveMode = $archiveMode;

    }

    /**
    * Returns the session ID, which uniquely identifies the session.
    */
    public function getSessionId()
    {
        return $this->sessionId;
    }

    /**
    * Returns the location hint IP address.
    *
    * See <a href="OpenTok.OpenTok.html#method_createSession">OpenTok->createSession()</a>.
    */
    public function getLocation()
    {
        return $this->location;
    }

    /**
    * Returns MediaMode::RELAYED if the session's streams will be transmitted directly between
    * peers; returns MediaMode::ROUTED if the session's streams will be transmitted using the
    * OpenTok Media Router.
    *
    * See <a href="OpenTok.OpenTok.html#method_createSession">OpenTok->createSession()</a>
    * and <a href="OpenTok.MediaMode.html">ArchiveMode</a>.
    */
    public function getMediaMode()
    {
        return $this->mediaMode;
    }

    /**
    * Defines whether the session is automatically archived (ArchiveMode::ALWAYS)
    * or not (ArchiveMode::MANUAL).
    *
    * See <a href="OpenTok.OpenTok.html#method_createSession">OpenTok->createSession()</a>
    * and <a href="OpenTok.ArchiveMode.html">ArchiveMode</a>.
    */
    public function getArchiveMode()
    {
        return $this->archiveMode;
    }

    /**
     * @internal
     */
    public function __toString()
    {
        return $this->sessionId;
    }

    /**
     * Creates a token for connecting to the session. In order to authenticate a user,
     * the client passes a token when connecting to the session.
     * <p>
     * For testing, you can also generate tokens or by logging in to your
     * <a href="https://tokbox.com/account">TokBox account</a>.
     *
     * @param array $options This array defines options for the token. This array include the
     * following keys, all of which are optional:
     *
     * <ul>
     *
     *    <li><code>'role'</code> (string) &mdash; One of the constants defined in the RoleConstants
     *    class. The default role is publisher</li>
     *
     *    <li><code>'expireTime'</code> (int) &mdash; The timestamp for when the token expires,
     *    in milliseconds since the Unix epoch. The default expiration time is 24 hours
     *    after the token creation time. The maximum expiration time is 30 days after the
     *    token creation time.</li>
     *
     *    <li><code>'data'</code> (string) &mdash; A string containing connection metadata
     *    describing the end-user. For example, you can pass the user ID, name, or other data
     *    describing the end-user. The length of the string is limited to 1000 characters.
     *    This data cannot be updated once it is set.</li>
     *
     * </ul>
     *
     * @return string The token string.
     */
    public function generateToken($options = array())
    {
        return $this->opentok->generateToken($this->sessionId, $options);
    }

}

/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

namespace OpenTok;

use OpenTok\Util\Client;
use OpenTok\Util\Validators;

use OpenTok\Exception\InvalidArgumentException;
use OpenTok\Exception\ArchiveUnexpectedValueException;

/**
* Represents data from a Sip Call
*
* @property string $id
* The unique identifier of the call that is created.
*
* @property string $connectionId
* The unique identifier of the connection that represents the SIP call in
* the session. You can use this value to disconnect the SIP call using the moderation API.
*
* @property string $streamId
* The id of the stream connected to the session streaming the audio received from the SIP call.
*/
class SipCall {

    /** @internal */
    private $data;

    /** @internal */
    public function __construct($sipCallData)
    {
        $this->data['id'] = $sipCallData['id'];
        $this->data['connectionId'] = $sipCallData['connectionId'];
        $this->data['streamId'] = $sipCallData['streamId'];
    }

    /**
    * Returns the conference ID.
    */
    /** @internal */
    public function __get($name)
    {
        switch($name) {
            case 'id':
            case 'connectionId':
            case 'streamId':
                return $this->data[$name];
                break;
            default:
                return null;
        }
    }


    /**
     * Returns a JSON representation of this SipCall object.
     */
    public function toJson()
    {
        return json_encode($this->data);
    }
}

/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "Archive List",
  "description": "A list of OpenTok Archives",
  "type": "object",
  "properties": {
    "count": {
      "type": "integer"
    },
    "items": {
      "type": "array",
      "items": {
        "$ref": "#/definitions/archive"
      }
    }
  },
  "required": ["count", "items"],
  "definitions": {
    "archive": {
      "title": "Archive",
      "description": "An OpenTok Archive",
      "type": "object",
      "properties": {
        "createdAt": {
          "type" : "integer"
        },
        "duration": {
          "type" : "integer",
          "minimum": 0
        },
        "id": {
          "type": "string",
          "pattern": "^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$"
        },
        "name": {
          "type": ["string", "null"]
        },
        "partnerId": {
          "type": "integer"
        },
        "reason": {
          "type": "string"
        },
        "sessionId": {
          "type": "string"
        },
        "size": {
          "type": "integer",
          "minimum" : 0
        },
        "status": {
          "type": "string",
          "enum": ["available", "started", "stopped", "failed", "deleted", "uploaded", "expired", "paused"]
        },
        "url": {
          "type": ["string", "null"]
        },
        "hasVideo": {
          "type": "boolean"
        },
        "hasAudio": {
          "type": "boolean"
        }
      },
      "required": [ "id", "partnerId", "sessionId", "status" ]
    }
  }
}
<?php

namespace OpenTok\Util;

/**
* @internal
*/
abstract class BasicEnum {
    private static $constCacheArray = null;

    private static function getConstants() {
        if (self::$constCacheArray === null) self::$constCacheArray = array();

        $calledClass = get_called_class();
        if (!array_key_exists($calledClass, self::$constCacheArray)) {
            $reflect = new \ReflectionClass($calledClass);
            self::$constCacheArray[$calledClass] = $reflect->getConstants();
        }

        return self::$constCacheArray[$calledClass];
    }

    public static function isValidName($name, $strict = false) {
        $constants = self::getConstants();

        if ($strict) {
            return array_key_exists($name, $constants);
        }

        $keys = array_map('strtolower', array_keys($constants));
        return in_array(strtolower($name), $keys);
    }

    public static function isValidValue($value) {
        $values = array_values(self::getConstants());
        return in_array($value, $values, $strict = true);
    }
}
/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "Broadcast",
  "description": "An OpenTok Broadcast",
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$"
    },
    "sessionId": {
      "type": "string"
    },
    "partnerId": {
      "type": "integer"
    },
    "createdAt": {
      "type" : "integer"
    },
    "updatedAt": {
      "type" : "integer"
    },
    "broadcastUrls": {
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "object",
          "properties": {
            "hls": {
              "type": "string"
            }
          }
        }
      ]
    }
  },
  "required": ["id", "sessionId", "partnerId", "createdAt", "updatedAt"]
}
<?php

namespace OpenTok\Util;

use \Guzzle\Http\Exception\ClientErrorResponseException;
use \Guzzle\Http\Exception\ServerErrorResponseException;

use OpenTok\Exception\Exception;
use OpenTok\Exception\DomainException;
use OpenTok\Exception\UnexpectedValueException;
use OpenTok\Exception\AuthenticationException;

use OpenTok\Exception\ArchiveException;
use OpenTok\Exception\ArchiveDomainException;
use OpenTok\Exception\ArchiveUnexpectedValueException;
use OpenTok\Exception\ArchiveAuthenticationException;

use OpenTok\Exception\BroadcastException;
use OpenTok\Exception\BroadcastDomainException;
use OpenTok\Exception\BroadcastUnexpectedValueException;
use OpenTok\Exception\BroadcastAuthenticationException;
use OpenTok\MediaMode;

// TODO: build this dynamically
/** @internal */
define('OPENTOK_SDK_VERSION', '3.0.0');
/** @internal */
define('OPENTOK_SDK_USER_AGENT', 'OpenTok-PHP-SDK/' . OPENTOK_SDK_VERSION);

/**
* @internal
*/
class Client extends \Guzzle\Http\Client
{
    protected $apiKey;
    protected $apiSecret;
    protected $configured = false;

    public function configure($apiKey, $apiSecret, $apiUrl)
    {
        $this->apiKey = $apiKey;
        $this->apiSecret = $apiSecret;
        $this->setBaseUrl($apiUrl);
        $this->setUserAgent(OPENTOK_SDK_USER_AGENT, true);

        // TODO: attach plugins
        $opentokAuthPlugin = new Plugin\OpentokAuth($apiKey, $apiSecret);
        $this->addSubscriber($opentokAuthPlugin);

        $this->configured = true;
    }

    public function isConfigured() {
        return $this->configured;
    }

    // General API Requests

    public function createSession($options)
    {
        $request = $this->post('/session/create');
        $request->addPostFields($this->postFieldsForOptions($options));
        try {
            $sessionXml = $request->send()->xml();
        } catch (\RuntimeException $e) {
            // The $response->xml() method uses the following code to throw a parse exception:
            // throw new RuntimeException('Unable to parse response body into XML: ' . $errorMessage);
            // TODO: test if we have a parse exception and handle it, otherwise throw again
            throw $e;
        } catch (\Exception $e) {
            $this->handleException($e);
            return;
        }
        return $sessionXml;
    }

    // Archiving API Requests

    public function startArchive($sessionId, $options)
    {
        // set up the request
        $request = $this->post('/v2/project/'.$this->apiKey.'/archive');
        $request->setBody(json_encode(array_merge(array( 'sessionId' => $sessionId ), $options)));
        $request->setHeader('Content-Type', 'application/json');

        try {
            $archiveJson = $request->send()->json();
        } catch (\Exception $e) {
            $this->handleArchiveException($e);
        }
        return $archiveJson;
    }

    public function stopArchive($archiveId)
    {
        // set up the request
        $request = $this->post('/v2/project/'.$this->apiKey.'/archive/'.$archiveId.'/stop');
        $request->setHeader('Content-Type', 'application/json');

        try {
            $archiveJson = $request->send()->json();
        } catch (\Exception $e) {
            // TODO: what happens with JSON parse errors?
            $this->handleArchiveException($e);
        }
        return $archiveJson;
    }

    public function getArchive($archiveId)
    {
        $request = $this->get('/v2/project/'.$this->apiKey.'/archive/'.$archiveId);
        try {
            $archiveJson = $request->send()->json();
        } catch (\Exception $e) {
            $this->handleException($e);
            return;
        }
        return $archiveJson;
    }

    public function deleteArchive($archiveId)
    {
        $request = $this->delete('/v2/project/'.$this->apiKey.'/archive/'.$archiveId);
        $request->setHeader('Content-Type', 'application/json');
        try {
            $response = $request->send();
            if ($response->getStatusCode() != 204) {
                $response->json();
            }
        } catch (\Exception $e) {
            $this->handleException($e);
            return false;
        }
        return true;
    }

    public function forceDisconnect($sessionId,$connectionId)
    {
        $request = $this->delete('/v2/project/'.$this->apiKey.'/session/'.$sessionId.'/connection/'.$connectionId);
        $request->setHeader('Content-Type', 'application/json');
        try {
            $response = $request->send();
            if ($response->getStatusCode() != 204) {
                $response->json();
            }
        } catch (\Exception $e) {
            $this->handleException($e);
            return false;
        }
        return true;
    }

    public function listArchives($offset, $count)
    {
        $request = $this->get('/v2/project/'.$this->apiKey.'/archive');
        if ($offset != 0) $request->getQuery()->set('offset', $offset);
        if (!empty($count)) $request->getQuery()->set('count', $count);
        try {
            $archiveListJson = $request->send()->json();
        } catch (\Exception $e) {
            $this->handleException($e);
            return;
        }
        return $archiveListJson;
    }

    public function startBroadcast($sessionId, $options)
    {
        $request = $this->post('/v2/project/'.$this->apiKey.'/broadcast');
        $request->setBody(json_encode(array(
            'sessionId' => $sessionId,
            'layout' => $options['layout']->jsonSerialize()
        )));
        $request->setHeader('Content-Type', 'application/json');

        try {
            $broadcastJson = $request->send()->json();
        } catch (\Exception $e) {
            $this->handleBroadcastException($e);
        }
        return $broadcastJson;
    }

    public function stopBroadcast($broadcastId)
    {
        $request = $this->post('/v2/project/'.$this->apiKey.'/broadcast/'.$broadcastId.'/stop');
        $request->setHeader('Content-Type', 'application/json');

        try {
            $broadcastJson = $request->send()->json();
        } catch (\Exception $e) {
            $this->handleBroadcastException($e);
        }
        return $broadcastJson;
    }

    public function getBroadcast($broadcastId)
    {
        $request = $this->get('/v2/project/'.$this->apiKey.'/broadcast/'.$broadcastId);
        try {
            $broadcastJson = $request->send()->json();
        } catch (\Exception $e) {
            $this->handleBroadcastException($e);
        }
        return $broadcastJson;
    }

    public function getLayout($resourceId, $resourceType = 'broadcast')
    {
        $request = $this->get('/v2/project/'.$this->apiKey.'/'.$resourceType.'/'.$resourceId.'/layout');
        try {
            $layoutJson = $request->send()->json();
        } catch (\Exception $e) {
            $this->handleException($e);
        }
        return $layoutJson;
    }

    public function updateLayout($resourceId, $layout, $resourceType = 'broadcast')
    {
        $request = $this->put('/v2/project/'.$this->apiKey.'/'.$resourceType.'/'.$resourceId.'/layout');
        $request->setBody(json_encode($layout->jsonSerialize()));
        $request->setHeader('Content-Type', 'application/json');
        try {
            $layoutJson = $request->send()->json();
        } catch (\Exception $e) {
            $this->handleException($e);
        }
        return $layoutJson;
    }

    public function updateStream($sessionId, $streamId, $properties)
    {
        $request = $this->put('/v2/project/'.$this->apiKey.'/session/'.$sessionId.'/stream/'.$streamId);
        $request->setBody(json_encode($properties));
        $request->setHeader('Content-Type', 'application/json');
        try {
            $response = $request->send();
            if ($response->getStatusCode() != 204) {
                $response->json();
            }
        } catch (\Exception $e) {
            $this->handleException($e);
        }
    }

    public function dial($sessionId, $token, $sipUri, $options)
    {
        $body = array(
          'sessionId' => $sessionId,
          'token' => $token,
          'sip' => array(
            'uri' => $sipUri,
            'secure' => $options['secure']
          )
        );

        if (isset($options) && array_key_exists('headers', $options) && sizeof($options['headers']) > 0) {
            $body['sip']['headers'] = $options['headers'];
        }

        if (isset($options) && array_key_exists('auth', $options)) {
            $body['sip']['auth'] = $options['auth'];
        }

        // set up the request
        $request = $this->post('/v2/project/'.$this->apiKey.'/call');
        $request->setBody(json_encode($body));
        $request->setHeader('Content-Type', 'application/json');

        try {
            $sipJson = $request->send()->json();
        } catch (\Exception $e) {
            $this->handleException($e);
        }
        return $sipJson;
    }

    // Helpers

    private function postFieldsForOptions($options)
    {
        $options['p2p.preference'] = empty($options['mediaMode']) ? MediaMode::ROUTED : $options['mediaMode'];
        unset($options['mediaMode']);
        if (empty($options['location'])) {
            unset($options['location']);
        }
        $options['api_key'] = $this->apiKey;
        return $options;
    }

    //echo 'Uh oh! ' . $e->getMessage();
    //echo 'HTTP request URL: ' . $e->getRequest()->getUrl() . "\n";
    //echo 'HTTP request: ' . $e->getRequest() . "\n";
    //echo 'HTTP response status: ' . $e->getResponse()->getStatusCode() . "\n";
    //echo 'HTTP response: ' . $e->getResponse() . "\n";

    private function handleException($e)
    {
        // TODO: test coverage
        if ($e instanceof ClientErrorResponseException) {
            // will catch all 4xx errors
            if ($e->getResponse()->getStatusCode() == 403) {
                throw new AuthenticationException(
                    $this->apiKey,
                    $this->apiSecret,
                    null,
                    $e
                );
            } else {
                throw new DomainException(
                    'The OpenTok API request failed: '. json_decode($e->getResponse()->getBody(true))->message,
                    null,
                    $e
                );
            }
        } else if ($e instanceof ServerErrorResponseException) {
            // will catch all 5xx errors
            throw new UnexpectedValueException(
                'The OpenTok API server responded with an error: ' . json_decode($e->getResponse()->getBody(true))->message,
                null,
                $e
            );
        } else {
            // TODO: check if this works because Exception is an interface not a class
            throw new \Exception('An unexpected error occurred');
        }
    }

    private function handleArchiveException($e)
    {
        try {
            $this->handleException($e);
        } catch (AuthenticationException $ae) {
            throw new ArchiveAuthenticationException($this->apiKey, $this->apiSecret, null, $ae->getPrevious());
        } catch (DomainException $de) {
            throw new ArchiveDomainException($e->getMessage(), null, $de->getPrevious());
        } catch (UnexpectedValueException $uve) {
            throw new ArchiveUnexpectedValueException($e->getMessage(), null, $uve->getPrevious());
        } catch (Exception $oe) {
            // TODO: check if this works because ArchiveException is an interface not a class
            throw new ArchiveException($e->getMessage(), null, $oe->getPrevious());
        }
    }

    private function handleBroadcastException($e)
    {
        try {
            $this->handleException($e);
        } catch (AuthenticationException $ae) {
            throw new BroadcastAuthenticationException($this->apiKey, $this->apiSecret, null, $ae->getPrevious());
        } catch (DomainException $de) {
            throw new BroadcastDomainException($e->getMessage(), null, $de->getPrevious());
        } catch (UnexpectedValueException $uve) {
            throw new BroadcastUnexpectedValueException($e->getMessage(), null, $uve->getPrevious());
        } catch (Exception $oe) {
            // TODO: check if this works because BroadcastException is an interface not a class
            throw new BroadcastException($e->getMessage(), null, $oe->getPrevious());
        }
    }

}
<?php

namespace OpenTok\Util\Plugin;

use \Firebase\JWT\JWT;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Guzzle\Common\Event;

/**
* @internal
*/
class OpentokAuth implements EventSubscriberInterface
{
    protected $apiKey;
    protected $apiSecret;

    public static function getSubscribedEvents()
    {
        return array('request.before_send' => 'onBeforeSend');
    }

    public function __construct($apiKey, $apiSecret)
    {
        $this->apiKey = $apiKey;
        $this->apiSecret = $apiSecret;
    }

    public function onBeforeSend(Event $event)
    {
        $request = $event['request'];
        $request->addHeader('X-OPENTOK-AUTH', $this->createAuthHeader());
    }

    private function createAuthHeader()
    {
        $token = array(
            'ist' => 'project',
            'iss' => $this->apiKey,
            'iat' => time(), // this is in seconds
            'exp' => time()+(5 * 60),
            'jti' => uniqid(),
        );
        return JWT::encode($token, $this->apiSecret);
    }
}
<?php

namespace OpenTok\Util;

use OpenTok\Util\Client;
use OpenTok\Layout;
use OpenTok\Role;
use OpenTok\MediaMode;
use OpenTok\ArchiveMode;
use OpenTok\OutputMode;
use OpenTok\OpenTok;

use OpenTok\Exception\InvalidArgumentException;

use JohnStevenson\JsonWorks\Document;
use JohnStevenson\JsonWorks\Utils as JsonUtils;

/**
* @internal
*/
class Validators
{
    static $guidRegEx = '/^\[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}\$/';
    static $archiveSchemaUri;
    static $broadcastSchemaUri;

    public static function validateApiKey($apiKey)
    {
        if (!(is_string($apiKey) || is_int($apiKey))) {
            throw new InvalidArgumentException(
                'The apiKey was not a string nor an integer: '.print_r($apiKey, true)
            );
        }
    }
    public static function validateApiSecret($apiSecret)
    {
        if (!(is_string($apiSecret))) {
            throw new InvalidArgumentException('The apiSecret was not a string: '.print_r($apiSecret, true));
        }
    }
    public static function validateApiUrl($apiUrl)
    {
        if (!(is_string($apiUrl) && filter_var($apiUrl, FILTER_VALIDATE_URL))) {
            throw new InvalidArgumentException(
                'The optional apiUrl was not a string: '.print_r($apiUrl, true)
            );
        }
    }
    public static function validateClient($client)
    {
        if (isset($client) && !($client instanceof Client)) {
            throw new InvalidArgumentException(
                'The optional client was not an instance of \OpenTok\Util\Client. client:'.print_r($client, true)
            );
        }
    }
    public static function validateSessionId($sessionId)
    {
        if(!is_string($sessionId) || empty($sessionId)){
            throw new InvalidArgumentException(
                'Null or empty session ID is not valid: '.print_r($sessionId, true)
            );
        }
    }
    public static function validateConnectionId($connectionId)
    {
        if(!is_string($connectionId) || empty($connectionId)){
            throw new InvalidArgumentException(
                'Null or empty connection ID is not valid: '.print_r($connectionId, true)
            );
        }
    }
    public static function validateRole($role)
    {
        if (!Role::isValidValue($role)) {
            throw new InvalidArgumentException('Unknown role: '.print_r($role, true));
        }
    }
    public static function validateExpireTime($expireTime, $createTime)
    {
        if(!is_null($expireTime)) {
            if(!is_numeric($expireTime)) {
                throw new InvalidArgumentException(
                    'Expire time must be a number: '.print_r($expireTime, true)
                );
            }
            if($expireTime < $createTime) {
                throw new InvalidArgumentException(
                    'Expire time must be in the future: '.print_r($expireTime, true).'<'.print_r($createTime, true)
                );
            }
            $in30Days = $createTime + 2592000;
            if($expireTime > $in30Days) {
                throw new InvalidArgumentException(
                    'Expire time must be in the next 30 days: '.print_r($expireTime, true).'>'.$in30Days
                );
            }
        }
    }
    public static function validateData($data)
    {
        if ($data != null) {
            if (!is_string($data)) {
                throw new InvalidArgumentException(
                    'Connection data must be a string. data:'.print_r($data, true)
                );
            }
            if(!empty($data)) {
                $dataLength = strlen($data);
                if($dataLength > 1000) {
                    throw new InvalidArgumentException(
                        'Connection data must be less than 1000 characters. Length: '.$dataLength
                    );
                }
            }
        }
    }
    public static function validateArchiveName($name)
    {
        if ($name != null && !is_string($name) /* TODO: length? */) {
            throw new InvalidArgumentException(
                'The name was not a string: '.print_r($name, true)
            );
        }
    }
    public static function validateArchiveHasVideo($hasVideo)
    {
        if (!is_bool($hasVideo)) {
            throw new InvalidArgumentException(
                'The hasVideo was not a boolean: '.print_r($hasVideo, true)
            );
        }
    }
    public static function validateArchiveHasAudio($hasAudio)
    {
        if (!is_bool($hasAudio)) {
            throw new InvalidArgumentException(
                'The hasAudio was not a boolean: '.print_r($hasAudio, true)
            );
        }
    }
    public static function validateArchiveOutputMode($outputMode)
    {
        if (!OutputMode::isValidValue($outputMode)) {
            throw new InvalidArgumentException('Unknown output mode: '.print_r($outputMode, true));
        }
    }
    public static function validateArchiveId($archiveId)
    {
        if ( !is_string($archiveId) || preg_match(self::$guidRegEx, $archiveId) ) {
            throw new InvalidArgumentException(
                'The archiveId was not valid. archiveId:'.print_r($archiveId, true)
            );
        }
    }
    public static function validateArchiveData($archiveData)
    {
        if (!self::$archiveSchemaUri) { self::$archiveSchemaUri = __DIR__.'/archive-schema.json'; }
        $document = new Document();
        // have to do a encode+decode so that json objects decoded as arrays from Guzzle
        // are re-encoded as objects instead
        $document->loadData(json_decode(json_encode($archiveData)));
        $document->loadSchema(self::$archiveSchemaUri);
        // JSON Pointers are supported for the validation using this library, this is a hack
        $document->loadSchema(JsonUtils::get(JsonUtils::get($document->schema->data, 'definitions'), 'archive'));
        if (!$document->validate()) {
            throw new InvalidArgumentException(
                'The archive data provided is not valid. Errors:'.$document->lastError.' archiveData:'.print_r($archiveData, true)
            );
        }
    }
    public static function validateArchiveListData($archiveListData)
    {
        if (!self::$archiveSchemaUri) { self::$archiveSchemaUri = __DIR__.'/archive-schema.json'; }
        $document = new Document();
        // have to do a encode+decode so that json objects decoded as arrays from Guzzle
        // are re-encoded as objects instead
        $document->loadData(json_decode(json_encode($archiveListData)));
        $document->loadSchema(self::$archiveSchemaUri);
        if (!$document->validate()) {
            throw new InvalidArgumentException(
                'The archive data provided is not valid. Errors:'.$document->lastError.' archiveData:'.print_r($archiveListData, true)
            );
        }
    }
    public static function validateOffsetAndCount($offset, $count)
    {
        if ((!is_numeric($offset) || $offset < 0 ) ||
            (($count != null && !is_numeric($count)) || $count < 0 || $count > 1000) ) {

            throw new InvalidArgumentException(
                'The offset or count were not valid numbers: offset='.print_r($offset, true).' count='.print_r($count, true)
            );
        }
    }
    public static function validateSessionIdBelongsToKey($sessionId, $apiKey)
    {
        self::validateSessionId($sessionId);
        $sessionIdParts = self::decodeSessionId($sessionId);
        if (!in_array($apiKey, $sessionIdParts)) {
            throw new InvalidArgumentException(
                'The sessionId must belong to the apiKey. sessionId: '.print_r($sessionId, true).', apiKey: '.print_r($apiKey, true)
            );
        }
    }
    public static function validateMediaMode($mediaMode)
    {
        if (!MediaMode::isValidValue($mediaMode)) {
            throw new InvalidArgumentException(
                'The media mode option must be either \'MediaMode::ROUTED\' or \'MediaMode::RELAYED\'. mediaMode:'.print_r($mediaMode, true)
            );
        }
    }
    public static function validateArchiveMode($archiveMode)
    {
        if (!ArchiveMode::isValidValue($archiveMode)) {
            throw new InvalidArgumentException(
                'The archive mode option must be either \'ArchiveMode::MANUAL\' or \'ArchiveMode::ALWAYS\'. archiveMode:'.print_r($archiveMode, true)
            );
        }
    }
    public static function validateLocation($location)
    {
        if ($location != null && !filter_var($location, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            throw new InvalidArgumentException(
                'The location option must be an IPv4 address. location:'.print_r($location, true)
            );
        }
    }
    public static function validateOpenTok($opentok)
    {
        if (!($opentok instanceof OpenTok)) {
            throw new InvalidArgumentException(
                'The opentok parameter must be an instance of OpenTok\OpenTok. opentok:'.print_r($opentok, true)
            );
        }
    }
    public static function validateBroadcastData($broadcastData)
    {
        if (!self::$broadcastSchemaUri) { self::$broadcastSchemaUri = __DIR__.'/broadcast-schema.json'; }
        $document = new Document();
        // have to do a encode+decode so that json objects decoded as arrays from Guzzle
        // are re-encoded as objects instead
        $document->loadData(json_decode(json_encode($broadcastData)));
        $document->loadSchema(self::$broadcastSchemaUri);
        if (!$document->validate()) {
            throw new InvalidArgumentException(
                'The broadcast data provided is not valid. Errors:'.$document->lastError.' broadcastData:'.print_r($broadcastData, true)
            );
        }
    }
    public static function validateBroadcastId($broadcastId)
    {
        if ( !is_string($broadcastId) || preg_match(self::$guidRegEx, $broadcastId) ) {
            throw new InvalidArgumentException(
                'The broadcastId was not valid. broadcastId:'.print_r($broadcastId, true)
            );
        }
    }
    public static function validateLayout($layout)
    {
        if (!($layout instanceof Layout)) {
            throw new InvalidArgumentException(
                'The layout parameter must be an instance of OpenTok\Layout. layout:'.print_r($layout, true)
            );
        }
    }
    public static function validateLayoutStylesheet($stylesheet)
    {
        if (!(is_string($stylesheet))) {
            throw new InvalidArgumentException('The stylesheet was not a string: '.print_r($stylesheet, true));
        }
    }
    public static function validateStreamId($streamId)
    {
        if (!(is_string($streamId))) {
            throw new InvalidArgumentException('The streamId was not a string: '.print_r($streamId, true));
        }
    }
    public static function validateLayoutClassList($layoutClassList, $format = 'JSON')
    {
        if ($format === 'JSON') {
            if (!is_array($layoutClassList) || self::isAssoc($layoutClassList)) {
                throw new InvalidArgumentException('The layoutClassList was not a valid JSON array: '.print_r($layoutClassList, true));
            }
        }
    }

    // Helpers

    // credit: http://stackoverflow.com/a/173479
    protected static function isAssoc($arr)
    {
        return array_keys($arr) !== range(0, count($arr) - 1);
    }

    protected static function decodeSessionId($sessionId)
    {
        $trimmedSessionId = substr($sessionId, 2);
        $parts = explode('-', $trimmedSessionId);
        $data = array();
        foreach($parts as $part) {
            $decodedPart = base64_decode($part);
            $dataItems = explode('~', $decodedPart);
            $data = array_merge($data, $dataItems);
        }
        return $data;
    }
}

/* vim: set ts=4 sw=4 tw=100 sts=4 et :*/
<?php

Phar::mapPhar('opentok.phar');

require_once('phar://'.__FILE__.DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'autoload.php');

__HALT_COMPILER();
(n+Bڬ H   GBMB